【问题标题】:EF Core 3.1 Query OptimizationEF Core 3.1 查询优化
【发布时间】:2020-05-18 23:56:12
【问题描述】:

我不时听到,EF Core 3.1 现在能够优化分组和几个聚合函数,如 sum 在数据库上执行。为什么我的 C# 代码会将此严重未优化的 SQL 代码发送到数据库服务器?

using var dbContext = new DatabaseContext();
var someTableData = await dbContext.SomeTable
    .GroupBy(x => x.Foobar)
    .Select(x => new { Foobar = x.Key, Quantity = x.Sum(y => y.Quantity) })
    .OrderByDescending(x => x.Quantity)
    .Take(10)
    .ToListAsync();

SQL(从实体框架日志复制):

SELECT [...] FROM public."SomeTable"

如您所见:查询不包含任何排序、分组、汇总……

我通过创建视图解决了我的问题,因为原始 ef 核心查询将应用程序的 RAM 使用量增加了 2GB(在 2 秒内),因为有很多表条目。但我的问题仍然存在:我做错了什么?是不是因为我使用的是 PostgreSQL (Npgsql.EntityFrameworkCore.PostgreSQL v3.1.0)?

【问题讨论】:

  • SomeTable 可能是一个视图吗?
  • 很可能是因为你使用的是Npgsql,因为它不是微软官方维护的,而是第三方Data Provider。而且由于数据提供者负责将 LINQ 表达式实际转换为任何内容(对您来说是 PSQL),因此 Npsql 数据提供者可能不支持这些功能(目前)。您听到的内容可能仅适用于 MS 开发的数据提供者
  • 您可以使用准备好的语句 (which are supported by Npgsql) 手动编写 SQL 查询然后执行它,这样您可以确保您的 SQL 查询按照您的意愿进行优化
  • 嗯,这表示客户端评估,因此似乎不是 EF Core 3.0+ 的输出。客户端评估已停用,如果 EF Core 无法翻译您的 GroupBy 查询或其中的某些部分,您应该获得运行时异常。 x.Foobar btw 的类型是什么?
  • @jarlh SomeTable 绝对是一张桌子。

标签: c# sql postgresql entity-framework-core ef-core-3.1


【解决方案1】:

我将您的查询弹出到一个快速测试工具中(请参阅下面所有 nuget 包版本的项目定义):

public class SomeTable
{
    public int Id { get; set; }
    public int Foobar { get; set; }
    public int Quantity { get; set; }
}

class MyDbContext : DbContext
{
    public DbSet<SomeTable> SomeTables { get; set; }
    public static readonly LoggerFactory DbCommandConsoleLoggerFactory
        = new LoggerFactory(new[] {
            new ConsoleLoggerProvider ((category, level) =>
                category == DbLoggerCategory.Database.Command.Name &&
                level == LogLevel.Trace, true)
        });
    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder.UseNpgsql("Server=...;Port=5432;Database=test;User Id=...;Password=...;")
        //optionsBuilder.UseSqlServer("Server=.\\SQLEXPRESS;Database=test;Trusted_Connection=true")
                    .UseLoggerFactory(DbCommandConsoleLoggerFactory)
                    .EnableSensitiveDataLogging();
        base.OnConfiguring(optionsBuilder);
    } 
}

class Program
{
    static void Main(string[] args)
    {
        var context = new MyDbContext();
        var someTableData = context.SomeTables
                .GroupBy(x => x.Foobar)
                .Select(x => new { Foobar = x.Key, Quantity = x.Sum(y => y.Quantity) })
                .OrderByDescending(x => x.Quantity)
                .Take(10);
        Console.Write(someTableData);
        Console.ReadKey();
    }
}
/*csproj file contents below:
<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp3.1</TargetFramework>
    <RootNamespace>ef_core3_playground</RootNamespace>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.EntityFrameworkCore" Version="3.1.1" />
    <PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="3.1.1" />
    <PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="3.1.1" />
    <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="3.1.1" />
    <PackageReference Include="Microsoft.Extensions.Logging.Console" Version="1.1.0" />
    <PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="3.1.1" />
  </ItemGroup>

</Project>
*/

我最终为各个提供商获得了以下 SQL:

--MS SQL
SELECT TOP(@__p_0) [s].[Foobar], SUM([s].[Quantity]) AS [Quantity]
FROM [SomeTables] AS [s]
GROUP BY [s].[Foobar]
ORDER BY SUM([s].[Quantity]) DESC

-- PG SQL
SELECT s."Foobar", SUM(s."Quantity")::INT AS "Quantity"
FROM "SomeTables" AS s
GROUP BY s."Foobar"
ORDER BY SUM(s."Quantity")::INT DESC
LIMIT @__p_0

这让我想知道是否可能是您的提供程序/EF 特定版本给了您这样的结果?虽然查看 diff between two npgsql releases 我看不到任何与该问题相关的内容,但我建议您尝试将所有与 EF 相关的软件包升级到 3.1.1 并重复您的测试。

【讨论】:

  • 我试试看。非常感谢您的调查!
猜你喜欢
  • 2022-01-14
  • 1970-01-01
  • 2021-01-20
  • 2021-01-30
  • 2021-12-12
  • 2021-09-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多