【问题标题】:EF Core 2.1 evaluates locally when subquery and aggregate after GroupingEF Core 2.1 在分组后进行子查询和聚合时在本地进行评估
【发布时间】:2018-07-13 09:30:25
【问题描述】:

因此,EF Core 2.1 在 SQL 服务器上评估 GroupBy LINQ 表达式(使用 SQL 提供程序时)。

这很棒但是当查询变得更复杂时我遇到了问题。

用于这些查询的模型是:

public class Invoice
{
    public string Status {get; set;}
    public string InvoiceType {get; set;}
    public decimal InvoicePayments {get; set;}
    public decimal EligibleValue {get; set;}
}

此 LINQ 语句完全在 SQL Server 中运行:

data
    .GroupBy(i => new { i.Status, i.InvoiceType })
    .Select(i => new 
    {
        i.Key, 
        Count = i.Count(), 
        Total = i.Sum(x => x.EligibleValue)
    });

并生成以下SQL

SELECT 
    [i].[Status], 
    [i].[InvoiceType], 
    COUNT(*) AS [Count], 
    SUM([i].[EligibleValue]) AS [Col1]
FROM [Invoice] AS [i]
GROUP BY [i].[Status], [i].[InvoiceType]

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

data
    .GroupBy(i => new { i.Status, i.InvoiceType })
    .Select(i => new 
    { 
        i.Key, 
        Count = i.Count(), 
        TotalLessThan100 = i.Where(x => x.InvoicePayments < 100).Sum(y => y.EligibleValue),
        TotalLessThan500 = i.Where(x => x.InvoicePayments < 500).Sum(z => z.EligibleValue)
    });

我在“输出”窗口中收到一些警告:

    The LINQ expression 'GroupBy(new <>f__AnonymousType0`2(Status = [i].Status, InvoiceType = [i].InvoiceType), [i])' could not be translated and will be evaluated locally.

The LINQ expression 'Count()' could not be translated and will be evaluated locally.

The LINQ expression 'where ([x].InvoicePayments < 100)' could not be translated and will be evaluated locally.

The LINQ expression 'where ([x].InvoicePayments < 500)' could not be translated and will be evaluated locally.

The LINQ expression 'Sum()' could not be translated and will be evaluated locally.

而且生成的SQL没有GroupBy,只有初始查询。

有什么方法可以定义这个查询在 SQL Server 上完全执行?

【问题讨论】:

  • 也许将.Where().Select()移出
  • 你知道我可以把它放在哪里吗?我需要在分组数据上评估 .Where()。我将编辑查询以显示更多完整场景 - 抱歉。
  • 您可以尝试使用.Sum(x =&gt; x.InvoicePayments &lt; 100 ? x.EligableValue : 0) 摆脱 where 子句。至少值得一试。
  • 感谢@Dirk,这是一个很好的建议,但它仍然给了我很好的旧警告The LINQ expression 'GroupBy(new &lt;&gt;f__AnonymousType0``2(Status = [i].Status, InvoiceType = [i].InvoiceType), [i])' could not be translated and will be evaluated locally.,然后是其他消息(尽管这次没有关于Where 的警告)

标签: c# sql-server linq entity-framework-core


【解决方案1】:

要遵循的第一条规则是在GroupBy 结果上避免WhereCount 的谓词版本,并尽可能使用条件Sum。 EF6 能够翻译此类结构,但 SQL 效率非常低。

所以通常你需要像这样重写查询:

data
    .GroupBy(i => new { i.Status, i.InvoiceType })
    .Select(g => new
    {
        g.Key,
        Count = g.Count(),
        TotalLessThan100 = g.Sum(i => i.InvoicePayments < 100 ? i.EligibleValue : 0),
        TotalLessThan500 = g.Sum(i => i.InvoicePayments < 500 ? i.EligibleValue : 0)
    });

但是 EF Core 2.1 GroupBy 翻译改进不包括 Sum 和一个简单的属性选择器,所以上面仍然使用客户端评估。很可能它将在将来的某个版本中修复,但在那之前,可以使用以下技巧 - 在 GroupBy 之前添加中间投影 (Select),其中包含以后需要的所有字段,包括计算的,然后在在GroupBy 之后聚合:

data
    .Select(i => new
    {
        i.Status,
        i.InvoiceType,
        LessThan100 = i.InvoicePayments < 100 ? i.EligibleValue : 0,
        LessThan500 = i.InvoicePayments < 500 ? i.EligibleValue : 0,
    })
    .GroupBy(i => new { i.Status, i.InvoiceType })
    .Select(g => new
    {
        g.Key,
        Count = g.Count(),
        TotalLessThan100 = g.Sum(i => i.LessThan100),
        TotalLessThan500 = g.Sum(i => i.LessThan500)
    });

翻译成:

SELECT [i].[Status], [i].[InvoiceType], COUNT(*) AS [Count], SUM(CASE
    WHEN [i].[InvoicePayments] < 100.0
    THEN [i].[EligibleValue] ELSE 0.0
END) AS [TotalLessThan100], SUM(CASE
    WHEN [i].[InvoicePayments] < 500.0
    THEN [i].[EligibleValue] ELSE 0.0
END) AS [TotalLessThan500]
FROM [Invoice] AS [i]
GROUP BY [i].[Status], [i].[InvoiceType]

【讨论】:

  • 谢谢@ivan!完美运行
  • 非常感谢。有趣的是,我并没有完全正确地做同样的事情,只是将 i.EligibleValue 添加到我的第一个 Select 中,然后将计算留在第二个 Select 的 Sum 中,这也可以正常工作。
【解决方案2】:

https://blogs.msdn.microsoft.com/dotnet/2018/05/30/announcing-entity-framework-core-2-1/

“我们现在支持在最常见的情况下将其转换为 SQL GROUP BY 子句。”

大概你的情况并不“常见”?

我在 2.0 中遇到过这个问题。我通过手工制作了很多生成 SQL 的方法来解决它。是的,它是一个 PITA,但出于各种其他原因,我想坚持使用 Core。

【讨论】:

  • 我猜“常见情况”是一个见仁见智的问题 ;-) 我不确定我们是否想走手工将GroupBy 转换为 SQL 语句的道路(但是感谢您的建议)。
  • 我看到了同样的台词,也有同样的感觉。对于任何类型的报告,这都是必不可少的。
猜你喜欢
  • 1970-01-01
  • 2017-12-27
  • 1970-01-01
  • 1970-01-01
  • 2020-01-17
  • 2023-02-08
  • 1970-01-01
  • 1970-01-01
  • 2015-05-30
相关资源
最近更新 更多