我已经从我的代码中简化了以下示例,并希望没有明显的编译错误。假设我有以下实体(不是我实际拥有的,请假设我没有EF或架构问题,这只是例如):
public class Company
{
public string GroupProperty {get;set;}
public virtual ICollection<PricingForm> PricingForms {get;set;}
}
public class PricingForm
{
public decimal Cost {get;set;}
}
我想这样查询:
IQueryable DynamicGrouping<T>(IQueryable<T> query)
{
Expression<Func<Company, decimal?>> exp = c => c.PricingForms.Sum(fr => fr.Cost);
string selector = "new (it.Key as Key, @0(it) as Value)";
IQueryable grouping = query.GroupBy("it.GroupProperty", "it").Select(selector, exp);
return grouping;
}
调用groupby / select行时出现以下错误:
System.Linq.Dynamic.ParseException: 'Argument list incompatible with lambda expression'
分组时,“它”是什么类型?我尝试使用其他表达式,假设它是IGrouping<string, Company>
或IQueryable<Company>
相同的错误。我试过选择“Cost”并将Sum()聚合移动到选择器字符串(即Sum(@0(it)) as Value
)并且似乎总是得到相同的错误。
我最终尝试了以下方面:
Expression<Func<IEnumerable<Company>, decimal?>> exp = l => l.SelectMany(c => c.PricingForms).Sum(fr => fr.Cost);
然而这一次,我走得更远但是当试图迭代结果时我得到了一个不同的错误。
The LINQ expression node type 'Invoke' is not supported in LINQ to Entities.
所以,通过这个动态分组并注入我自己的select表达式,我应该假设'it'的数据类型是什么?这甚至会起作用吗?
答案 0 :(得分:2)
it
的类型为IGrouping<TKey, TElement>
,其中TKey
基于keySelector
结果类型是动态的,TElement
是输入的元素类型IQueryable
。幸运的是IGrouping<TKey, TElement>
继承了(是)IEnumerable<TElement>
,所以一旦你知道输入元素类型,你就可以安全地在IEnumerable<TElement>
上建立选择。
换句话说,基于Expression<Func<IEnumerable<Company>, decimal?>>
的最后一次尝试是正确的。
您收到的新错误是因为@0(it)
生成了Expression.Invoke
来电,而EF不支持这种电话。解决此问题的最简单方法是使用LINQKit Expand
方法:
Expression<Func<Company, decimal?>> exp = c => c.PricingForms.Sum(fr => fr.Cost);
string selector = "new (it.Key as Key, @0(it) as Value)";
IQueryable grouping = query.GroupBy("it.GroupProperty", "it").Select(selector, exp);
// This would fix the EF invocation expression error
grouping = grouping.Provider.CreateQuery(grouping.Expression.Expand());
return grouping;