2017-02-03 65 views
2

這是我的問題Dynamic Expression Generation Issues with ValueTypes的後續添加一個新變量:實體框架。現在我能夠在處理ValueType時生成必要的表達式,當Linq-to-Entities試圖處理查詢時,我遇到了一個新問題。我收到以下錯誤:使用Linq到實體的盒裝值

System.NotSupportedException: Unable to cast the type 'System.Int32' to type 'System.Object'. LINQ to Entities only supports casting EDM primitive or enumeration types.

Linq-to-Entities顯然不是盒裝值的粉絲。當我強制查詢進行處理時(通過ToList()或其他類型的方法),但當數據庫完成時,這是非常理想的。

有沒有辦法使這種方法更通用,使Linq-Entities快樂?請記住,我不知道屬性的類型,直到運行時。

public IEnumerable<Expression<Func<T, object>>> GetExpressions<T>(string sortedColumn) where T : IReportRecord 
{ 
    var columns = GetFullSortOrder(sortedColumn); 
    var typeParameter = Expression.Parameter(typeof(T)); 
    foreach (var c in columns) 
    { 
     var propParameter = Expression.Property(typeParameter, c); 
     if (propParameter.Type.IsValueType) 
     { 
      var boxedPropParameter = Expression.Convert(propParameter, typeof(object)); 
      yield return Expression.Lambda<Func<T, object>>(boxedPropParameter, typeParameter); 
     } 
     else 
     { 
      yield return Expression.Lambda<Func<T, object>>(propParameter, typeParameter); 
     } 
    } 
} 
+1

代替生成的'Expressions'您可以動態地直接申請您的訂購到'IQueryable'這樣的:http://stackoverflow.com/questions/41244/dynamic-linq-orderby-on-ienumerablet/233505#233505 – Aducci

回答

2

其實這個問題是你產生Expression<Func<T, object>>當需要Expression.Convert以前的相反。考慮具有以下簽名

public static IOrderedQueryable<T> OrderBy<T>(
    this IQueryable<T> source, 
    IEnumerable<Expression<Func<T, object>>> selectors) 

,其具有以形成從通過選擇器OrderBy/ThenBy鏈的方法。這裏您需要刪除Expression.Convert只需要使Expression<Func<T, V>>轉換爲Expression<Func<T, object>>值爲V

讓我們創建一個小的輔助方法,兩個轉換:

public static Expression Wrap(this Expression source) 
{ 
    if (source.Type.IsValueType) 
     return Expression.Convert(source, typeof(object)); 
    return source; 
} 

public static LambdaExpression Unwrap<T>(this Expression<Func<T, object>> source) 
{ 
    var body = source.Body; 
    if (body.NodeType == ExpressionType.Convert) 
     body = ((UnaryExpression)body).Operand; 
    return Expression.Lambda(body, source.Parameters); 
} 

現在原方法的實現可以是簡單地

public static IEnumerable<Expression<Func<T, object>>> GetExpressions<T>(string sortedColumn) where T : IReportRecord 
{ 
    var typeParameter = Expression.Parameter(typeof(T)); 
    return from c in GetFullSortOrder(sortedColumn) 
      select Expression.Lambda<Func<T, object>>(
       Expression.Property(typeParameter, c).Wrap(), typeParameter); 
} 

和應用方法可以是這樣的

public static IOrderedQueryable<T> OrderBy<T>(this IQueryable<T> source, IEnumerable<Expression<Func<T, object>>> keySelectors) 
{ 
    var result = source.Expression; 
    var method = "OrderBy"; 
    foreach (var item in keySelectors) 
    { 
     var keySelector = item.Unwrap(); 
     result = Expression.Call(
      typeof(Queryable), method, new[] { typeof(T), keySelector.Body.Type }, 
      result, Expression.Quote(keySelector)); 
     method = "ThenBy"; 
    } 
    return (IOrderedQueryable<T>)source.Provider.CreateQuery(result); 
} 

當然,它不需要那樣。在你的情況下,你可以將兩種方法合併爲一種(類似於第二種方法,但接收string sortedColumn),在這種情況下,您將簡單地使用非通用Expression.Lambda方法,而不包含Convert的值類型。

+1

這正是我所需要的,完美!謝謝伊萬!唯一的區別在於,我沒有使用擴展方法,而是使用'GetExpressions'方法按照您的建議返回非通用版本。游泳! – JNYRanger