【问题标题】:Translating query with GROUP BY and COUNT to Linq将带有 GROUP BY 和 COUNT 的查询转换为 Linq
【发布时间】:2019-09-26 06:09:38
【问题描述】:

我有一个查询来查看用户插入了多少实体 (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]

这将返回我想要的。我尝试在使用 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();

但是这会导致整个表被加载并且计算在内存中完成:

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:警告:无法翻译 LINQ 表达式“where ([s].Version > 1)”,将在本地进行评估。 Microsoft.EntityFrameworkCore.Query:警告:无法翻译 LINQ 表达式“Count()”并将在本地进行评估。 Microsoft.EntityFrameworkCore.Query:警告:无法翻译 LINQ 表达式“where ([s].Version > 1)”,将在本地进行评估。 Microsoft.EntityFrameworkCore.Query:警告:无法翻译 LINQ 表达式“Count()”,将在本地计算。

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

有没有一种不同的写法可以转化为我之前发布的 SQL 查询之类的东西?

【问题讨论】:

    标签: c# linq entity-framework-core


    【解决方案1】:

    避免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();
    

    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();
    

    【讨论】:

    • 我们为什么要避免Count的谓词版本?
    • @JonathanWood 因为它没有直接的 SQL 等价物(而有条件的 Sum 有),所以它要么没有被翻译(到目前为止的 EF Core),要么变得更糟且效率低下的翻译(EF6)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-01-17
    • 1970-01-01
    • 2016-11-25
    • 1970-01-01
    • 2021-12-19
    • 2014-04-20
    • 1970-01-01
    相关资源
    最近更新 更多