【问题标题】:LINQ does not produce COUNT() query on group using EF CoreLINQ 不会使用 EF Core 对组产生 COUNT() 查询
【发布时间】:2018-08-24 15:19:37
【问题描述】:

我有一个 ASP.NET Core 应用程序,它通过 EF Core 在 SQL Server 2012 上运行。我想要实现的是计算不同组的大小,例如

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

问题是整个事情非常慢,原因似乎是SQL语句没有映射到GROUP BYCOUNT(*),但是根据调试器,应用程序请求所有内容并且在 CPU 上执行计数。我从调试器的事件窗口得到的查询如下:

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

编辑:我在“真实”EF 和 ctx.Database.Log = Console.Write 上尝试了相同的查询,这会产生预期的 COUNT() 查询,这让我相信这是 EF Core 的问题.

【问题讨论】:

  • 在选择前执行let count = g.Count() 有帮助吗?我会假设它没有,但你可以尝试一下,然后在 new { ... } 表达式中使用 count 变量。
  • 很遗憾没有。查询看起来完全一样。
  • 我做了一些进一步的研究,似乎这是 EF Core 的一个已知限制:stackoverflow.com/questions/40557003/…
  • 好的,看来我的 EF Core 版本太旧了。 2.1.2 版产生预期的查询。
  • 如果您愿意,请将您的答案添加为已接受的答案。这将使社区受益。

标签: c# entity-framework-core


【解决方案1】:

请将 EF Core 升级到 2.1.2 版本,现在支持。

【讨论】:

    【解决方案2】:

    无法翻译 LINQ 表达式“GroupBy([x.Device], [x])” 并将在本地进行评估。

    无法翻译 LINQ 表达式“Count()”并将 本地评估。

    以上日志来自我后来的测试结果,显示GroupBy&Count()尚不支持将完整的LINQ转换为SQL,然后直接在数据库中执行完整的SQL,现在EF Core必须首先获取所有数据,然后在本地获取 GroupBy 结果。所以,如果你的数据很大,那么性能会很差。

    您必须硬编码一个简单的 SQL 才能直接在数据库中执行您的工作,然后性能将恢复正常。

    我升级到 EF Core 2.1.2,在MySQLMS 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# 代码通过this tool 生成以下日志:

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

    这里是核心 LOGS,显示 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]
    

    【讨论】:

    • 这个答案令人困惑。您能否更新顶部的 cmets 以进一步解释一下?为了我的利益,我将不胜感激。
    • 已更新。尚不支持 GroupByCount()。您必须改用 SQL。
    • 很抱歉,此答案不正确或可能具有误导性。如您所见,here -> GroupBy() 在 EF Core 2.1 及更高版本中确实受支持。但是,它目前很可能只支持简单的 GroupBy()。而且我不知道 Count() 是什么时候添加的,但据我所知它就在那里。
    • @AdamVincent 是的,我今天下午看到了文档,但实际上用我提到的工具测试它很容易:weblogs.asp.net/dixin/…
    猜你喜欢
    • 2020-03-29
    • 2020-01-25
    • 2021-05-03
    • 2019-11-09
    • 2023-02-08
    • 2019-12-17
    • 2021-10-21
    • 2021-02-22
    • 2021-04-27
    相关资源
    最近更新 更多