使用 GROUP BY 和 COUNT 将查询转换为 Linq

Val*_*tor 8 c# linq entity-framework-core

我有一个查询来查看用户插入了多少个实体 ( Version = 1) 以及他们更新了多少个实体( Version > 1)。它通过记录的用户名查询整个表和组。这是原始 SQL 查询:

SELECT 
    [s.InternalUser].[UserName],
    COUNT(CASE WHEN s.Version = 1 THEN 1 END) AS [InsertCount],
    COUNT(CASE WHEN s.Version > 1 THEN 1 END) AS [UpdateCount]
FROM [Sale] AS [s]
INNER JOIN [InternalUser] AS [s.InternalUser] ON [s].[InternalUserId] = 
    [s.InternalUser].[InternalUserId]
GROUP BY [s.InternalUser].[UserName]
Run Code Online (Sandbox Code Playgroud)

这将返回我想要的。我尝试将其转换为使用 EF Core 2.2 的项目中的 Linq 查询:

var countData = await _context.Sale
.GroupBy(s => s.InternalUser.UserName)
.Select(g => new
{
    UserName = g.Key,
    InsertCount = g.Count(s => s.Version == 1),
    UpdateCount = g.Count(s => s.Version > 1)
})
.ToListAsync();
Run Code Online (Sandbox Code Playgroud)

然而,这会导致整个表被加载并在内存中完成计算:

Microsoft.EntityFrameworkCore.Query:警告:LINQ 表达式“GroupBy([s.InternalUser].UserName, [s])”无法翻译,将在本地进行评估。Microsoft.EntityFrameworkCore.Query:警告:LINQ 表达式“where ([s].Version == 1)”无法翻译,将在本地进行评估。Microsoft.EntityFrameworkCore.Query:警告:无法翻译 LINQ 表达式“Count()”,将在本地进行评估。Microsoft.EntityFrameworkCore.Query:警告:LINQ 表达式“where ([s].Version == 1)”无法翻译,将在本地进行评估。Microsoft.EntityFrameworkCore.Query:警告:无法翻译 LINQ 表达式“Count()”,将在本地进行评估。Microsoft.EntityFrameworkCore.Query:Warning: The LINQ expression 'where ([s].Version > 1)' 无法翻译,将在本地进行评估。Microsoft.EntityFrameworkCore.Query:警告:无法翻译 LINQ 表达式“Count()”,将在本地进行评估。Microsoft.EntityFrameworkCore.Query:警告:LINQ 表达式“where ([s].Version > 1)”无法翻译,将在本地进行评估。Microsoft.EntityFrameworkCore.Query:警告:无法翻译 LINQ 表达式“Count()”,将在本地进行评估。无法翻译,将在本地进行评估。Microsoft.EntityFrameworkCore.Query:警告:无法翻译 LINQ 表达式“Count()”,将在本地进行评估。无法翻译,将在本地进行评估。Microsoft.EntityFrameworkCore.Query:警告:无法翻译 LINQ 表达式“Count()”,将在本地进行评估。

Count()是导致它的查询,如果我删除 Group By 被转换为查询。

是否有不同的编写方式可以转换为我之前发布的 SQL 查询之类的内容?

Iva*_*oev 15

避免谓词版本Count并使用等效的条件Sum

在 EF Core 3.0+ 中,您可以直接替换Count(condition)Sum(condition ? 1 : 0),例如

var countData = await _context.Sale
    .GroupBy(s => s.InternalUser.UserName)
    .Select(g => new
    {
        UserName = g.Key,
        InsertCount = g.Sum(s => s.Version == 1 ? 1 : 0),
        UpdateCount = g.Sum(s => s.Version > 1 ? 1 : 0),
    })
    .ToListAsync();
Run Code Online (Sandbox Code Playgroud)

EF Core 2.x 仅支持GroupBy对简单分组元素属性访问器上的聚合进行转换,因此您需要使用GroupBy带元素选择器的重载来预选所需的表达式,例如

var countData = await _context.Sale
    .GroupBy(s => s.InternalUser.UserName, s => new
    {
        InsertCount = s.Version == 1 ? 1 : 0,
        UpdateCount = s.Version > 1 ? 1 : 0,
    })
    .Select(g => new
    {
        UserName = g.Key,
        InsertCount = g.Sum(s => s.InsertCount),
        UpdateCount = g.Sum(s => s.UpdateCount),
    })
    .ToListAsync();
Run Code Online (Sandbox Code Playgroud)

  • @JonathanWood 因为它没有直接的 SQL 等效项(而条件 `Sum` 有),所以它要么没有被翻译(EF Core 到目前为止),要么得到更糟糕和低效的翻译(EF6)。 (2认同)