EF Core 2.1 在 Sum complex 和 Grouping 时在本地求值

EF Core 2.1 evaluates locally when Sum complex and Grouping

我正在使用 EF Core 2.1,查询不会在 SQL 服务器端计算。

此查询中使用的模型是:

public class V_TurnoverByDivision
{      
    public long Id { get; set; }
    public decimal LineAmount { get; set; }
    public DateTime? PostingDate { get; set; }
    public decimal Quantity { get; set; }
    public decimal UnitCostLcy { get; set; }
    public string DivisionCode { get; set; }
    public string DivisionDescription { get; set; }
    public string TopDivisionCode { get; set; }
    public string TopDivisionDescription { get; set; }
    public decimal RUCAmount { get; set; }
}

这条 LINQ 语句 运行 完全在 SQL 服务器:

return query
  .GroupBy(g => new { g.DivisionCode, g.DivisionDescription, g.TopDivisionCode, g.TopDivisionDescription, g.PostingDate })
  .Select(s =>
                    new V_TurnoverByDivision
                    {
                        DivisionCode = s.Key.DivisionCode,
                        DivisionDescription = s.Key.DivisionDescription,
                        TopDivisionCode = s.Key.TopDivisionCode,
                        TopDivisionDescription = s.Key.TopDivisionDescription,
                        PostingDate = s.Key.PostingDate,
                        LineAmount = s.Sum(ss => ss.LineAmount),
                        RUCAmount = s.Sum(ss => ss.LineAmount - (ss.Quantity * ss.UnitCostLcy))
                    });

并生成以下 SQL

SELECT
    [v].[BIInvCNLinesID]
   ,[v].[DivisionCode]
   ,[v].[DivisionDescription]
   ,[v].[LineAmount]
   ,[v].[PostingDate]
   ,[v].[Quantity]
   ,[v].[TopDivisionCode]
   ,[v].[TopDivisionDescription]
   ,[v].[UnitCostLcy]
FROM [V_TurnoverByDivision] AS [v]
WHERE [v].[PostingDate] >= @__firstDayOfcurrentMonth_0
ORDER BY [v].[DivisionCode], [v].[DivisionDescription], [v].[TopDivisionCode], [v].[TopDivisionDescription], [v].[PostingDate]

此 LINQ 语句有效但在内存中执行 GroupBy

我在输出中收到警告 windows

Microsoft.EntityFrameworkCore.Query:警告:无法翻译 LINQ 表达式 'Sum()',将在本地求值。

但是当我使用这个查询时

                  return query
                .GroupBy(g => new { g.DivisionCode, g.DivisionDescription, g.TopDivisionCode, g.TopDivisionDescription, g.PostingDate })
                .Select(s =>
                    new V_TurnoverByDivision
                    {
                        DivisionCode = s.Key.DivisionCode,
                        DivisionDescription = s.Key.DivisionDescription,
                        TopDivisionCode = s.Key.TopDivisionCode,
                        TopDivisionDescription = s.Key.TopDivisionDescription,
                        PostingDate = s.Key.PostingDate,
                        LineAmount = s.Sum(ss => ss.LineAmount)
                    });
            };

和 SQL 生成的查询应该是:

SELECT
    [v].[DivisionCode]
   ,[v].[DivisionDescription]
   ,[v].[TopDivisionCode]
   ,[v].[TopDivisionDescription]
   ,[v].[PostingDate]
   ,SUM([v].[LineAmount]) AS [LineAmount]
FROM [V_TurnoverByDivision] AS [v]
WHERE [v].[PostingDate] >= @__firstDayOfcurrentMonth_0
GROUP BY [v].[DivisionCode]
        ,[v].[DivisionDescription]
        ,[v].[TopDivisionCode]
        ,[v].[TopDivisionDescription]
        ,[v].[PostingDate]

如何解决问题:

RUCAmount = s.Sum(ss => ss.LineAmount - (ss.Quantity * ss.UnitCostLcy))

这是 EF Core GroupBy 翻译限制(可能会在未来的某个版本中解决)。为了可以翻译成 SQL,聚合方法表达式应该是简单的 属性 访问器。

这就是为什么

s.Sum(ss => ss.LineAmount)

翻译,但是

s.Sum(ss => ss.LineAmount - (ss.Quantity * ss.UnitCostLcy))

没有。

因此解决方案是预先 select 聚合所需的表达式。一种方法是对元素 select 或:

使用 GroupBy 重载
return query
   .GroupBy(e => new // Key
   { 
       e.DivisionCode,
       e.DivisionDescription,
       e.TopDivisionCode,
       e.TopDivisionDescription,
       e.PostingDate
   },
   e => new // Element
   {
       e.LineAmount,
       RUCAmount = e.LineAmount - (e.Quantity * e.UnitCostLcy) // <--
   })
  .Select(g => new V_TurnoverByDivision
  {
      DivisionCode = g.Key.DivisionCode,
      DivisionDescription = g.Key.DivisionDescription,
      TopDivisionCode = g.Key.TopDivisionCode,
      TopDivisionDescription = g.Key.TopDivisionDescription,
      PostingDate = g.Key.PostingDate,
      LineAmount = g.Sum(e => e.LineAmount),
      RUCAmount = g.Sum(e => e.RUCAmount) // <--
  });