GroupBy query by Linq.Expressions and Lambdas
本文关键字:and Lambdas Expressions Linq query by GroupBy | 更新日期: 2023-09-27 18:09:00
我需要通过Linq.Expressions表示此查询:
db.Documents.GroupBy(a => 1).Select(b => b.Sum(c => c.Amount) });
这是我到目前为止所拥有的:
IQueryable<Document> data = db.Documents;
ParameterExpression pe = Expression.Parameter(typeof(Document), "doc");
Expression groupBy = Expression.Call(
typeof(Queryable),
"GroupBy",
new Type[] { typeof(Document), typeof(int) },
data.Expression,
Expression.Lambda(Expression.Constant(1), pe));
ParameterExpression peg = Expression.Parameter(typeof(IGrouping<int, Document>), "group");
Expression select = Expression.Call(
typeof(Queryable),
"Select",
new Type[] { typeof(IGrouping<int, Document>), typeof(int) },
groupBy,
Expression.Lambda(Expression.Property(peg, "Key"), peg));
foreach (var item in data.Provider.CreateQuery(select)) { ... }
这是实现:
db.Documents.GroupBy(a => 1).Select(b => b.Key });
而且它工作得很好。现在,我想聚合一个总和,而不是访问组的键。
这就是对我来说变得棘手的地方。我在想这样的事情:
ParameterExpression pe1 = Expression.Parameter(typeof(Document), "other");
Expression sum = Expression.Call(
typeof(Queryable),
"Sum",
new Type[] { typeof(Document) },
peg,
Expression.Lambda(Expression.Property(pe1, "Amount"), pe1));
此外,对于 Sum 函数
...b.Sum(c => c.Amount)
智能感知给出签名:
IEnumerable<Document>.Sum<Document>(Func<Document, decimal> selector)
而对于:
db.Documents.Sum(a => a.Amount)
我得到:
IQueryable<Document>.Sum<Document>(Expression<Func<Document, decimal>> selector)
选择器在一个版本中是 Func,在另一个版本中是表达式。我不知道如何在 Linq 表达式中处理 Func。也许智能感知是错误的?
聚合源的表达式是我最大的问题。通过查看:
...b.Sum(c => c.Amount)
我假设 b 应该是 IGrouping('select' 的参数表达式(,这应该是 Sum 的来源,但这不会编译。我不知道还能尝试什么?
以下是最后选择表达式的外观:
Expression Select = Expression.Call(
typeof(Queryable),
"Select",
new Type[] { typeof(IGrouping<int, Document>), typeof(decimal?) },
GroupBy,
Expression.Lambda(sum, peg));
但我什至无法达到这一点,因为失败的"总和"表达式。
任何指示将不胜感激。
问候
智能感知还可以。让我们看看:
db.Documents.GroupBy(a => 1).Select(b => b.Sum(c => c.Amount) });
(1( db.Documents
类型IQueryable<Document>
(2( a
类型Document
(3(db.Documents.GroupBy(a => 1)
类型IQueryable<IGrouping<int, Document>>
(4(b
类型是IGrouping<int, Document>
,而IEnumerable<Document>
(5(c
类型Document
这也意味着GroupBy
和Select
方法来自Queryable
,而Sum
来自Enumerable
。
如何在MethodCall
表达式中区分Func<...>
和Expression<Func<...>>
,规则很简单。在这两种情况下,您都使用 Expression.Lambda<Func<...>>
来创建Expression<Func<...>>
,然后如果调用需要Func<...>
则直接传递它,如果该方法需要Expression<Func<...>>
则用 Expression.Quote
包装它。
话虽如此,让我们构建示例查询表达式:
var query = db.Documents.AsQueryable();
// query.GroupBy(a => 1)
var a = Expression.Parameter(typeof(Document), "a");
var groupKeySelector = Expression.Lambda(Expression.Constant(1), a);
var groupByCall = Expression.Call(typeof(Queryable), "GroupBy",
new Type[] { a.Type, groupKeySelector.Body.Type },
query.Expression, Expression.Quote(groupKeySelector));
// c => c.Amount
var c = Expression.Parameter(typeof(Document), "c");
var sumSelector = Expression.Lambda(Expression.PropertyOrField(c, "Amount"), c);
// b => b.Sum(c => c.Amount)
var b = Expression.Parameter(groupByCall.Type.GetGenericArguments().Single(), "b");
var sumCall = Expression.Call(typeof(Enumerable), "Sum",
new Type[] { c.Type },
b, sumSelector);
// query.GroupBy(a => 1).Select(b => b.Sum(c => c.Amount))
var selector = Expression.Lambda(sumCall, b);
var selectCall = Expression.Call(typeof(Queryable), "Select",
new Type[] { b.Type, selector.Body.Type },
groupByCall, Expression.Quote(selector));
// selectCall is our expression, let test it
var result = query.Provider.CreateQuery(selectCall);