Gin*_*Tez 3 c# linq sql-server entity-framework-core
因此,EF Core 2.1会评估SQL Server上的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;}
}
Run Code Online (Sandbox Code Playgroud)
此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)
});
Run Code Online (Sandbox Code Playgroud)
并生成以下SQL
SELECT
[i].[Status],
[i].[InvoiceType],
COUNT(*) AS [Count],
SUM([i].[EligibleValue]) AS [Col1]
FROM [Invoice] AS [i]
GROUP BY [i].[Status], [i].[InvoiceType]
Run Code Online (Sandbox Code Playgroud)
此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)
});
Run Code Online (Sandbox Code Playgroud)
我在"输出"窗口中收到一些警告:
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.
Run Code Online (Sandbox Code Playgroud)
并且生成的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)
});
Run Code Online (Sandbox Code Playgroud)
但是,除了简单的属性选择器之外,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)
});
Run Code Online (Sandbox Code Playgroud)
这被翻译成:
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]
Run Code Online (Sandbox Code Playgroud)