因此 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
和谓词版本的 Count
在 GroupBy
结果上使用条件 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/