【发布时间】:2018-03-11 09:06:45
【问题描述】:
我正在为 IQueryable<TSource> 创建 Lambda 表达式,以下是我的扩展方法代码,我需要调用它:
queryableData.GroupBy<int,Product>("ID")
queryableData.GroupBy<string,Product>("Name")
public static IQueryable<IGrouping<TKey,TSource>> GroupBy<TKey,TSource>(this IQueryable<TSource> queryable, string propertyName)
{
// Access the propertyInfo, using Queryable Element Type (Make it Case insensitive)
var propInfo = queryable.ElementType.GetProperty(propertyName,BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance);
// Access the Collection / Queryable Type
var collectionType = queryable.ElementType;
// Creating Group Parameter Expression
var groupParameterExpression = Expression.Parameter(collectionType, "g");
// Create MemberEXpression with the Property (access the property of a Type)
var propertyAccess = Expression.MakeMemberAccess(groupParameterExpression, propInfo);
// Create Lambda Expression
var lambdaExpression = Expression.Lambda<Func<TSource,TKey>>(propertyAccess, groupParameterExpression);
// Return GroupBy result
return queryable.GroupBy(lambdaExpression);
}
我的目标是生成Expression<Func<TSource,object>>而不是Expression<Func<TSource,TKey>>,这样就可以在不提供Key类型的情况下调用它,代码如下:
public static IQueryable<IGrouping<object, TSource>> GroupByObjectKey<TSource>(this IQueryable<TSource> queryable, string propertyName)
{
// Access the propertyInfo, using Queryable Element Type (Make it Case insensitive)
var propInfo = queryable.ElementType.GetProperty(propertyName, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance);
// Access the Collection / Queryable Type
var collectionType = queryable.ElementType;
// Creating Group Parameter Expression
var groupParameterExpression = Expression.Parameter(collectionType, "g");
// Create MemberEXpression with the Property (access the property of a Type)
var propertyAccess = Expression.MakeMemberAccess(groupParameterExpression, propInfo);
// Create Lambda Expression
var lambdaExpression = Expression.Lambda<Func<TSource, object>>(propertyAccess, groupParameterExpression);
// Return GroupBy result
return queryable.GroupBy(lambdaExpression);
}
现在我可以使它适用于字符串类型,如下所示:
queryableData.GroupBy<Product>("Name")
但它在以下调用整数类型时失败,如下所述:
queryableData.GroupBy<Product>("Id")
Expression of type 'System.Int32' cannot be used for return type 'System.Object'
这是一个类型转换的明显案例,但我很惊讶为什么一个类型会拒绝转换为 Object 基类,可能是什么原因,任何指针/建议
【问题讨论】:
-
第二个可以,但我不知道为什么所有东西都需要转换为 Object 类型。它适用于引用类型,但不适用于原始类型。
-
我会按照“此处需要显式转换”的方式来回答,但不能说比Eric Lippert here 更好; derives from 与 convertible to 不同:“纠正这个迷思的方法是简单地将“derives from”替换为“is convertible to”,并忽略指针类型:C# 中的每个非指针类型都可转换为对象"
-
这是表达式的陷阱之一:值类型装箱需要明确的
Convert指令。它类似于编译器在您编写诸如return (object)0;之类的代码时为您执行的操作。只需接受值类型不同的事实,并顺其自然。 -
其他评论者已经提到您需要将值类型返回表达式 (
propertyAccess) 与Expression.Convert包装起来。这将允许您成功构建您的查询。但请注意,如果您打算在 EF6 查询中使用它,您将得到简单的运行时异常 无法将类型“System.Int32”转换为类型“System.Object”。 LINQ to Entities 仅支持转换 EDM 基元或枚举类型。。一般来说,方差不适用于值类型。GroupBy有TKey参数是有原因的。
标签: c# linq expression-trees iqueryable