LINQ не создает запрос COUNT () для группы, использующей EF Core - PullRequest
0 голосов
/ 24 августа 2018

У меня есть приложение ASP.NET Core, которое работает на SQL Server 2012 через EF Core.Чего я хочу добиться, так это подсчитать размер различных групп, таких как

from b in this._context.Benchmark
group b by b.Device into g
select new {
    Device = g.Key,
    Count = g.Count()
}

. Проблема в том, что все это очень медленно, и причина этого в том, что оператор SQL не сопоставляется сGROUP BY и COUNT(*), но, согласно отладчику, приложение запрашивает все и выполняет подсчет на процессоре.Запрос, который я получаю из окна событий отладчика, выглядит следующим образом:

SELECT [b0].[ID], [b0].[CreateDate], [b0].[Creator], [b0].[Device], [b0].[Environment], [b0].[Machine], [b0].[Name], [b0].[Plugin], [b0].[RunDate]
FROM [Benchmark] AS [b0]
ORDER BY [b0].[Device]

Я также вижу, что потребление памяти IIS Express, которое я использую для отладки, безумно для простого COUNT(), поэтому я склоненсчитаю, что это фактический запрос.

Вопрос: как я могу перефразировать запрос так, чтобы он фактически отображался в COUNT()?

Редактировать : Iпопробовал тот же самый запрос на «реальном» EF и ctx.Database.Log = Console.Write, который производит ожидаемый запрос COUNT(), что заставляет меня поверить, что это проблема EF Core.

Ответы [ 2 ]

0 голосов
/ 24 августа 2018

Не удалось перевести выражение LINQ «GroupBy ([x.Device], [x])», и оно будет оцениваться локально.

Невозможно выполнить выражение LINQ «Count ()»переведен и будет оцениваться локально.

Приведенные выше журналы взяты из моих более поздних результатов испытаний, которые показывают, что GroupBy & Count() пока не поддерживаются для перевода полного LINQ в SQL, а затем непосредственно выполняютсяполный SQL в базе данных, теперь EF Core должен сначала получить все данные, а затем GroupBy результаты локально.Итак, если ваши данные огромны, то производительность будет очень плохой.

Вам придется жестко программировать простой SQL, чтобы напрямую работать с базой данных, тогда производительность вернется к норме.

Я обновился до EF Core 2.1.2, попробовал следующий код в MySQL и MS SQL:

var query = _context.Benchmark.GroupBy(x => x.Device, 
              (key, group) => new  //Result selector
              {
                  Device = key,
                  Count = group.Count()
              });
await query.ToListAsync();

Селектор результатов находится прямо внутри GroupBy, что приведет куверен, может ли он быть переведен на правильный SQL, но, к сожалению, это НЕ.

Вот еще один хитрый способ, избегайте использования Count:

var query = _context.Benchmark.GroupBy(x => x.Device, 
              (key, group) => new  //Result selector
              {
                  Device = key,
                  Count = group.Select(g => g.Id)//Avoid to use Count
              });
await query.ToListAsync();

Код C # создает следующие журналы этот инструмент :

2018-08-24T14:27:26.6737424-04:00 Information 10403 Microsoft.EntityFrameworkCore.Infrastructure
Entity Framework Core 2.1.2-rtm-30932 initialized 'ApplicationDbContext' using provider 'Pomelo.EntityFrameworkCore.MySql' with options: None
2018-08-24T14:27:31.4270317-04:00 Debug 10101 Microsoft.EntityFrameworkCore.Query
Compiling query model: 
'from IGrouping<Device, Benchmark> <generated>_0 in 
    (from Benchmark x in DbSet<Benchmark>
    select [x]).GroupBy([x].Device, [x])
select new <>f__AnonymousType3<Device, int>(
    [<generated>_0].Key, 

        (from Benchmark <generated>_1 in [<generated>_0]
        select [<generated>_1]).Count()
)'
2018-08-24T14:27:31.4319437-04:00 Debug 10104 Microsoft.EntityFrameworkCore.Query
Optimized query model: 
'from IGrouping<Device, Benchmark> <generated>_0 in 
    (from Benchmark x in DbSet<Benchmark>
    join Device x.Device in DbSet<Device>
    on Property([x], "DeviceId") equals Property([x.Device], "Id")
    select [x]).GroupBy([x.Device], [x])
select new <>f__AnonymousType3<Device, int>(
    [<generated>_0].Key, 

        (from Benchmark <generated>_1 in [<generated>_0]
        select [<generated>_1]).Count()
)'

Вот основные ЛОГИ, которые показывают, что GROUPBY пока не поддерживается:

2018-08-24T14:27:31.4431635-04:00 Warning 20500 Microsoft.EntityFrameworkCore.Query
The LINQ expression 'GroupBy([x.Device], [x])' could not be translated and will be evaluated locally.
2018-08-24T14:27:31.4476637-04:00 Warning 20500 Microsoft.EntityFrameworkCore.Query
The LINQ expression 'Count()' could not be translated and will be evaluated locally.
2018-08-24T14:27:31.4511652-04:00 Warning 20500 Microsoft.EntityFrameworkCore.Query
The LINQ expression 'Count()' could not be translated and will be evaluated locally.

Вот журналы отдыха после:

2018-08-24T14:27:31.4608060-04:00 Debug 10107 Microsoft.EntityFrameworkCore.Query
(QueryContext queryContext) => IAsyncEnumerable<<>f__AnonymousType3<Device, int>> _InterceptExceptions(
    source: IAsyncEnumerable<<>f__AnonymousType3<Device, int>> _SelectAsync(
        source: IAsyncEnumerable<IGrouping<Device, ValueBuffer>> _GroupBy(
            source: IAsyncEnumerable<TransparentIdentifier<ValueBuffer, Device>> _ShapedQuery(
                queryContext: queryContext, 
                shaperCommandContext: SelectExpression: 
                    SELECT `x.Device`.`Id`, `x.Device`.`Description`, `x.Device`.`IP`, `x.Device`.`Name`, `x.Device`.`Port`
                    FROM `Benchmark` AS `x`
                    INNER JOIN `Device` AS `x.Device` ON `x`.`DeviceId` = `x.Device`.`Id`
                    ORDER BY `x.Device`.`Id`, 
                shaper: TypedCompositeShaper<ValueBufferShaper, ValueBuffer, BufferedOffsetEntityShaper<Device>, Device, TransparentIdentifier<ValueBuffer, Device>>), 
            keySelector: (TransparentIdentifier<ValueBuffer, Device> t0) => t0.Inner, 
            elementSelector: (TransparentIdentifier<ValueBuffer, Device> t0) => t0.Outer), 
        selector: (IGrouping<Device, ValueBuffer> <generated>_0 | CancellationToken ct) => Task<<>f__AnonymousType3<Device, int>> _ExecuteAsync(
            taskFactories: new Func<Task<object>>[]{ () => Task<object> _ToObjectTask(Task<int> Count(
                        source: IAsyncEnumerable<ValueBuffer> _ToAsyncEnumerable(<generated>_0), 
                        cancellationToken: queryContext.CancellationToken)) }, 
            selector: (object[] results) => new <>f__AnonymousType3<Device, int>(
                <generated>_0.Key, 
                (int)results[0]
            ))), 
    contextType: Sequencer.Updater.Data.ApplicationDbContext, 
    logger: DiagnosticsLogger<Query>, 
    queryContext: queryContext)
2018-08-24T14:27:31.4825759-04:00 Debug 20000 Microsoft.EntityFrameworkCore.Database.Connection
Opening connection to database 'TestDB' on server 'localhost,3306'.
2018-08-24T14:27:31.4877302-04:00 Debug 20001 Microsoft.EntityFrameworkCore.Database.Connection
Opened connection to database 'TestDB' on server 'localhost,3306'.
2018-08-24T14:27:31.4901269-04:00 Debug 20100 Microsoft.EntityFrameworkCore.Database.Command
Executing DbCommand [Parameters=[], CommandType='Text', CommandTimeout='30']
SELECT `x.Device`.`Id`, `x.Device`.`Description`, `x.Device`.`IP`, `x.Device`.`Name`, `x.Device`.`Port`
FROM `Benchmark` AS `x`
INNER JOIN `Device` AS `x.Device` ON `x`.`DeviceId` = `x.Device`.`Id`
ORDER BY `x.Device`.`Id`
2018-08-24T14:27:31.4929857-04:00 Information 20101 Microsoft.EntityFrameworkCore.Database.Command
Executed DbCommand (3ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
SELECT `x.Device`.`Id`, `x.Device`.`Description`, `x.Device`.`IP`, `x.Device`.`Name`, `x.Device`.`Port`
FROM `Benchmark` AS `x`
INNER JOIN `Device` AS `x.Device` ON `x`.`DeviceId` = `x.Device`.`Id`
ORDER BY `x.Device`.`Id`
2018-08-24T14:27:31.5231128-04:00 Debug 20300 Microsoft.EntityFrameworkCore.Database.Command
A data reader was disposed.
2018-08-24T14:27:31.5270399-04:00 Debug 20002 Microsoft.EntityFrameworkCore.Database.Connection
Closing connection to database 'TestDB' on server 'localhost,3306'.
2018-08-24T14:27:31.5303748-04:00 Debug 20003 Microsoft.EntityFrameworkCore.Database.Connection
Closed connection to database 'TestDB' on server 'localhost,3306'.

Вот мое окружение:

PM> dotnet --info
.NET Core SDK (reflecting any global.json):
 Version:   2.1.401
 Commit:    91b1c13032

Runtime Environment:
 OS Name:     Windows
 OS Version:  10.0.16299
 OS Platform: Windows
 RID:         win10-x64
 Base Path:   C:\Program Files\dotnet\sdk\2.1.401\

Host (useful for support):
  Version: 2.1.3
  Commit:  124038c13e

.NET Core SDKs installed:
  2.1.202 [C:\Program Files\dotnet\sdk]
  2.1.302 [C:\Program Files\dotnet\sdk]
  2.1.400 [C:\Program Files\dotnet\sdk]
  2.1.401 [C:\Program Files\dotnet\sdk]

.NET Core runtimes installed:
  Microsoft.AspNetCore.All 2.1.2 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
  Microsoft.AspNetCore.All 2.1.3 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
  Microsoft.AspNetCore.App 2.1.2 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 2.1.3 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.NETCore.App 2.0.9 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 2.1.2 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 2.1.3 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
0 голосов
/ 24 августа 2018

Пожалуйста, обновите EF Core до версии 2.1.2, теперь он поддерживается.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...