c# - EF Core 2.1 在分组后进行子查询和聚合时在本地进行评估

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

因此 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 和谓词版本的 CountGroupBy 结果上使用条件 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]

关于c# - EF Core 2.1 在分组后进行子查询和聚合时在本地进行评估,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51322006/

相关文章:

sql-server - 如何从数据库中获取用户定义变量的值?

SQL 查询根据同一表中其他列的值更新列

sql - 生成唯一名称的性能问题

c# - 使用一个 SQL 表的三向连接

c# - 为什么 Entity Framework 6.x 不缓存结果?

java - 自动生成Word文档

c# - 如何使用poco实体生成器

c# - 亡灵形态仍在产生事件

c# - linq/lambda 查询中的双内连接?

c# - 加入 DataTable 与 List<SomeObject>