在 LINQ 中重用 Lambda Select 片段

Nei*_*ski 4 c# linq lambda entity-framework-core

我希望能够在我的 Entity Framework Core 2.0 查询中重用我选择的 lambda 表达式的片段

例如:

var result = await ctx.Customers
  .Select(cust => new CustomerDto {
    CustomerId = cust.Id,
    CustomerName = cust.Name,
    CurrentValue = cust.Orders
      .Where(order => order.OrderDate >= DateTime.Now.AddDays(-30)
      .Sum(order => order.TotalValue)
    })
    .ToListAsync();
Run Code Online (Sandbox Code Playgroud)

由于我可能想计算CurrentValue其他查询中的属性(实际上子查询比这更复杂),我理想情况下希望将上述代码重构为:

var result = await ctx.Customers
  .Select(cust => new CustomerDto {
    CustomerId = cust.Id,
    CustomerName = cust.Name,
    CurrentValue = CalculateCustomerCurrentValueExpr(cust)
  })
  .ToListAsync();
Run Code Online (Sandbox Code Playgroud)

我已经使用 a创建了 Linq谓词Linq.Expression,但是我一直无法找到将 anExpression作为 select 语句元素的方法。

任何帮助将非常感激。

更新 - .AsExpandable()/.Invoke() 的性能

对于任何感兴趣的人,我运行了十次测试代码,结果如下:

Standard Inline Code: 17ms (58,609 ticks) With .AsExpandable() and inline code 16ms (58,029 ticks) With .AsExpandable() and .Invoke() 16ms (58,224 ticks)

我怀疑如果运行了更多的测试周期,所有三个场景的平均处理时间将是相同的——至少在我可以测量的准确度水平上(simple StopWatch())。

感谢所有贡献者,特别是 SergeyA 的解决方案和 Ivan Stoev 的简单解释 .AsExpandable()

Ser*_*eyA 5

您可以使用来自 LinqKit 库 ( http://www.albahari.com/nutshell/linqkit.aspx ) 的AsExpandable 扩展重用表达式。

例子:

Expression<Func<Customer,long>> func = c => c.Orders
  .Where(order => order.OrderDate >= DateTime.Now.AddDays(-30)
  .Sum(order => order.TotalValue);

var result = await ctx.Customers
  .AsExpandable() // this allow to unwrap injected expression
  .Select(cust => new CustomerDto {
    CustomerId = cust.Id,
    CustomerName = cust.Name,
    CurrentValue = func.Invoke(cust) // this inject predefined expression
  })
  .ToListAsync(); 
Run Code Online (Sandbox Code Playgroud)

  • @Neilski `Invoke` 是一个永远不会被执行的自定义方法。将其视为已知的“占位符”。`AsExpandable` 允许对最终查询表达式树进行预处理,找到该占位符并将其替换为实际表达式(展开)。将其视为等效于字符串替换的表达式。与 EF Core 查询转换、执行和具体化过程相比,性能影响可以忽略不计。 (4认同)