将聚合 select 表达式传递给 Dynamic Linq 的 GroupBy
Passing an aggregate select expression to Dynamic Linq's GroupBy
我已从我的代码中简化了以下示例,希望不会因此出现明显的编译错误。假设我有以下实体(不是我实际拥有的实体,请假设我没有 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'
分组后"it"是什么类型?我尝试使用其他假设它是 IGrouping<string, Company>
或 IQueryable<Company>
的表达式,同样的错误。我试过 selecting "Cost" 并将 Sum() 聚合移动到 selector 字符串(即 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' 的数据类型是什么?这还能用吗?
it
的类型是IGrouping<TKey, TElement>
,其中TKey
是基于keySelector
结果类型的动态,TElement
是元素类型输入 IQueryable
。幸运的是 IGrouping<TKey, TElement>
继承(是一个)IEnumerable<TElement>
,所以只要知道输入元素类型,就可以安全地将选择器基于 IEnumerable<TElement>
.
也就是说,最后一次尝试Expression<Func<IEnumerable<Company>, decimal?>>
是正确的。
您收到的新错误是因为 @0(it)
生成 Expression.Invoke
call which is not supported by EF. The easiest way to fix that is to use 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;
我已从我的代码中简化了以下示例,希望不会因此出现明显的编译错误。假设我有以下实体(不是我实际拥有的实体,请假设我没有 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'
分组后"it"是什么类型?我尝试使用其他假设它是 IGrouping<string, Company>
或 IQueryable<Company>
的表达式,同样的错误。我试过 selecting "Cost" 并将 Sum() 聚合移动到 selector 字符串(即 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' 的数据类型是什么?这还能用吗?
it
的类型是IGrouping<TKey, TElement>
,其中TKey
是基于keySelector
结果类型的动态,TElement
是元素类型输入 IQueryable
。幸运的是 IGrouping<TKey, TElement>
继承(是一个)IEnumerable<TElement>
,所以只要知道输入元素类型,就可以安全地将选择器基于 IEnumerable<TElement>
.
也就是说,最后一次尝试Expression<Func<IEnumerable<Company>, decimal?>>
是正确的。
您收到的新错误是因为 @0(it)
生成 Expression.Invoke
call which is not supported by EF. The easiest way to fix that is to use 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;