Linq.Expressions和Lambdas的GroupBy查询

时间:2016-09-27 15:40:24

标签: c# linq lambda expression

我需要的是通过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)

Intellisense给出签名:

IEnumerable<Document>.Sum<Document>(Func<Document, decimal> selector)

同时:

db.Documents.Sum(a => a.Amount)

我明白了:

IQueryable<Document>.Sum<Document>(Expression<Func<Document, decimal>> selector)

Selector在一个版本中是Func,在另一个版本中是Expression。我不知道如何在Linq Expressions中处理Func。也许Intellisense错了?

汇总来源的表达是我最大的问题。看看:

...b.Sum(c => c.Amount)

我认为b应该是IGrouping(参数表示&#39;选择&#39;),这应该是Sum的源,但不会编译。 我不知道还有什么可以尝试?

以下是最后一个选择表达式的样子:

Expression Select = Expression.Call(
    typeof(Queryable),
    "Select",
    new Type[] { typeof(IGrouping<int, Document>), typeof(decimal?) },
    GroupBy,
    Expression.Lambda(sum, peg));

但是我甚至无法达到这一点,因为失败的总和&#39;表达。

任何指针都会受到赞赏。

问候,

1 个答案:

答案 0 :(得分:1)

智能感知是好的。让我们看看:

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>which in turn is IEnumerable<Document>
(5)c类型为Document

这也意味着GroupBySelect方法来自QueryableSum来自Enumerable

如何在Func<...>表达式中区分Expression<Func<...>>MethodCall,规则很简单。在这两种情况下,您都使用Expression.Lambda<Func<...>>来创建Expression<Func<...>>,然后如果调用需要Func<...>,则直接传递它,如果方法需要Expression<Func<...>>,那么请用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); 包裹它{3}}

话虽如此,让我们构建样本查询表达式:

.Object-to-center {
    position: absolute;
    left: 50%;
    transform: translateX(-50%);
}