2017-02-03 43 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