因此,EF Core 2.1在SQL服务器上(使用SQL提供程序时)评估GroupBy LINQ表达式。
这太棒了 当查询变得更加复杂时,我遇到了一个问题。
这些查询使用的模型是:
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上完全执行?
答案 0 :(得分:8)
要遵循的第一条规则是避免在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
,而不仅仅是简单的属性选择器,因此上述内容仍使用客户端评估。它很可能会在将来的某些发行版中得到修复,但是在此之前,可以使用以下技巧-在Select
之前添加中间投影(GroupBy
),其中包含稍后需要的所有字段,包括计算所得的字段,然后在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]
答案 1 :(得分:1)
https://blogs.msdn.microsoft.com/dotnet/2018/05/30/announcing-entity-framework-core-2-1/
“在大多数情况下,我们现在支持将其转换为SQL GROUP BY子句。”
大概您的案子不是'普通'的吗?
我在2.0中有这个问题。我通过手工制作许多方法来生成SQL来解决它。是的,它是一个PITA,但出于其他各种原因,我想坚持使用Core。