这是我的问题Dynamic Expression Generation Issues with ValueTypes的后续内容,它添加了一个新变量:Entity Framework。现在我能够在处理ValueTypes时生成必要的表达式,当Linq-to-Entities尝试处理查询时,我遇到了一个新问题。我收到以下错误:
System.NotSupportedException:无法转换类型' System.Int32'键入' System.Object'。 LINQ to Entities仅支持转换EDM原语或枚举类型。
显然,Linq-to-Entities并不是盒装价值观的粉丝。当我强制查询处理(通过ToList()
或其他类型方法)时,这是有效的,但是当数据库完成时,这是理想的。
有没有办法让这个方法更通用,以使Linq-to-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);
}
}
}
答案 0 :(得分:2)
实际上,此问题与之前生成Expression.Convert
时需要Expression<Func<T, object>>
的问题相反。考虑具有以下签名的方法
public static IOrderedQueryable<T> OrderBy<T>(
this IQueryable<T> source,
IEnumerable<Expression<Func<T, object>>> selectors)
必须从传递的选择器创建OrderBy
/ ThenBy
链。在这里,您需要删除所需的Expression.Convert
,才能将价值类型Expression<Func<T, V>>
的{{1}}转换为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
方法而不用{{包装值类型1}}。