Перевод запроса с GROUP BY и COUNT в Linq - PullRequest
3 голосов
/ 25 сентября 2019

У меня есть запрос, чтобы узнать, сколько сущностей было добавлено пользователями (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]

Это возвращает то, что я хочу.Я попытался перевести это на запрос Linq в проекте с использованием EF Core 2.2:

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 ()' не может быть переведено и будет оценено локально.

Это запрос Count(), который вызывает его, если я удалю этоGroup By преобразуется в запрос.

Есть ли другой способ написания этого, который переводил бы что-то вроде SQL-запроса, который я опубликовал ранее?

1 Ответ

1 голос
/ 26 сентября 2019

Избегайте предикатной версии 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();
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...