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));

但我什至无法达到这一点,因为失败的"总和"表达式。

任何指示将不胜感激。

问候

GroupBy query by Linq.Expressions and Lambdas

智能感知还可以。让我们看看:

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

这也意味着GroupBySelect方法来自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);