2016-11-11 47 views
1

我的存儲庫方法從數據庫中提取東西。它接受的排序順序作爲參數:如何指定謂詞的類型,直到運行時我纔會知道?

IEnumerable<Car> getCars<TSortKey>(Expression<Func<Car, TSortKey>> sort); 

我用TSortKey,因爲我不知道哪個屬性將被使用,直到運行時,它可能是x => x.Namex => x.Make這是字符串,但它也可能是x => x.History.Age這是一個整數。

用戶選擇排序順序,然後我在交換機中設置排序謂詞並調用該方法。

Expression<Func<Car, object>> sortPredicate; 
    switch (sortOption) { 
    case SortOption.Name: sortPredicate = s => s.Name; break; 
    case SortOption.Make: sortPredicate = s => s.Make; break; 
    case SortOption.Age: sortPredicate = s => s.History.Age; break; 
    default:    sortPredicate = s => s.Name; break; 
    } 
    var cars = repo.getCars(sortPredicate); 

我在謂語使用object,因爲我不知道的類型,直到運行時。但是這會產生錯誤的SQL,並拋出。

那麼我該如何解決這個問題?

+0

可能的解決方案:我可以將方法調用到每個開關的情況下,但變得非常混亂。例如'case SortOption.Name:cars = repo.getCars(x => x.Name); break;' – grokky

+1

我很確定你的可能解決方案是最好的。另一種可能性就是將你的'SortOption'枚舉傳入回購並構建正確的lamda .. – Jamiec

+0

@Jamiec是的我現在正在做「雜亂」的方式,但它變得非常混亂,因爲這只是一小段摘錄我的真實代碼。我希望我能以某種方式重寫lambda來代替泛型。 – grokky

回答

2

問題是Expression<Func<T, object>>爲值類型屬性生成額外的Convert,EF不喜歡並拋出NotSupportedException

而不是OrderBy,您可以在您的存儲庫類中使用以下輔助方法。它所做的是,如果需要剝離Convert表達和動態調用Queryable.OrderBy方法:

public static partial class EFExtensions 
{ 
    public static IOrderedQueryable<T> SortBy<T>(this IQueryable<T> source, Expression<Func<T, object>> keySelector) 
    { 
     var body = keySelector.Body; 
     if (body.NodeType == ExpressionType.Convert) 
      body = ((UnaryExpression)keySelector.Body).Operand; 
     var selector = Expression.Lambda(body, keySelector.Parameters); 
     var orderByCall = Expression.Call(
      typeof(Queryable), "OrderBy", new[] { typeof(T), body.Type }, 
      source.Expression, Expression.Quote(selector)); 
     return (IOrderedQueryable<T>)source.Provider.CreateQuery(orderByCall); 
    } 
} 
+1

這真的很棒。我還添加了一個'SortByDescending'方法。 – grokky

+0

是否將此表達式重寫爲「slow」作爲反射?我不假設,因爲你不是查詢類型和元數據,而是創建新的東西。那是對的嗎? – grokky

+0

我確信你可以做到這一點!雖然你也可以在上面的方法中添加'bool descending'參數並在裏面使用''OrderBy「+(降序?」降序「:」「)':) –

相關問題