我试图用表达式树模拟这个lambda调用:
myList.AsQueryable().GroupBy(g=>g.Name).Select(s => s.FirstOrDefault());
到目前为止,我来到这里:
public Expression Distinct (IQueryable queryable, string propertyName)
{
var propInfo = queryable.ElementType.GetProperty(propertyName);
var collectionType = queryable.ElementType;
var groupParameterExpression = Expression.Parameter(collectionType, "g");
var propertyAccess = Expression.MakeMemberAccess(groupParameterExpression, propInfo);
var groupLambda = Expression.Lambda(propertyAccess, groupParameterExpression);
var groupExpression = Expression.Call(typeof(Queryable), "GroupBy", new Type[] { collectionType, propInfo.PropertyType }, queryable.Expression, groupLambda);
var selectParameterExpression = Expression.Parameter(groupExpression.Type, "s");
var selectFirstOrDefaultMethodExpression = Expression.Call(typeof(Enumerable), "FirstOrDefault", new Type[] { collectionType }, selectParameterExpression);
var selectLambda = Expression.Lambda(selectFirstOrDefaultMethodExpression, selectParameterExpression);
return Expression.Call(typeof(Queryable), "Select", new Type[] { groupExpression.Type, selectParameterExpression.Type }, groupExpression, selectLambda);
}
这部分可以接受:
var groupParameterExpression = Expression.Parameter(collectionType, "g");
var propertyAccess = Expression.MakeMemberAccess(groupParameterExpression, propInfo);
var groupLambda = Expression.Lambda(propertyAccess, groupParameterExpression);
var groupExpression = Expression.Call(typeof(Queryable), "GroupBy", new Type[] { collectionType, propInfo.PropertyType }, queryable.Expression, groupLambda);
我在groupExpression
中获得了一个有效的表达式,并且在这种情况下从模型中调用type
属性时收到Name
:
IGrouping<string, Product>
其中,Product是一个如下所示的模型:
public class Product
{
public string Name { get; set; }
}
selectParameterExpression
是一个类型为IGrouping<string,Product>
的参数表达式。
当我尝试在此处拨打FirstOrDefault
时出现异常:
var selectFirstOrDefaultMethodExpression = Expression.Call(typeof(Enumerable), "FirstOrDefault", new Type[] { collectionType }, selectParameterExpression);
没有通用的方法&#39; FirstOrDefault&#39; on type&#39; System.Linq.Enumerable&#39;与提供的类型参数和参数兼容。如果方法是非泛型的,则不应提供类型参数。
答案 0 :(得分:2)
该代码存在几个问题。
首先,在使用Expression.Call
调用Queryable
方法时,您必须使用Expression.Quote
包含lambdas,以便将它们视为Expression<Func<...>>
而不仅仅Func<...>
{1}}。
其次,当您传递Select
IGrouping<TKey, TElement>
时,示例中的groupExpression.Type
参数类型应为IQueryable<IGrouping<TElement, TKey>>
,即您需要提取IGrouping
部分,例如:
groupExpression.Type.GetGenericArguments().Single()
最后,Select
调用通用参数应为IGrouping<TKey, TElement>
,TElement
,例如可以从selectParameterExpression.Type
,selectLambda.Body.Type
获取。
所以工作方法可能是这样的:
public Expression Distinct(IQueryable queryable, string propertyName)
{
var propInfo = queryable.ElementType.GetProperty(propertyName);
var collectionType = queryable.ElementType;
var groupParameterExpression = Expression.Parameter(collectionType, "g");
var propertyAccess = Expression.MakeMemberAccess(groupParameterExpression, propInfo);
var groupLambda = Expression.Lambda(propertyAccess, groupParameterExpression);
var groupExpression = Expression.Call(typeof(Queryable), "GroupBy", new Type[] { collectionType, propInfo.PropertyType }, queryable.Expression, Expression.Quote(groupLambda));
var selectParameterExpression = Expression.Parameter(groupExpression.Type.GetGenericArguments().Single(), "s");
var selectFirstOrDefaultMethodExpression = Expression.Call(typeof(Enumerable), "FirstOrDefault", new Type[] { collectionType }, selectParameterExpression);
var selectLambda = Expression.Lambda(selectFirstOrDefaultMethodExpression, selectParameterExpression);
return Expression.Call(typeof(Queryable), "Select", new Type[] { selectParameterExpression.Type, selectLambda.Body.Type }, groupExpression, Expression.Quote(selectLambda));
}