Оптимизация запросов EF Core 3.1

Время от времени я слышу, что EF Core 3.1 теперь может оптимизировать группировку и несколько агрегатных функций, таких как сумма для выполнения в базе данных. Почему мой код 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 (скопировано из журнала Entity Framework):

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

Как видите: запрос не содержит упорядочивания, группировки, суммирования, ...

Я решил свою проблему, создав представление, поскольку исходный запрос ядра ef увеличил использование ОЗУ приложением на 2 ГБ (за 2 секунды), поскольку в таблице было много записей. Но мой вопрос остается: что я сделал не так? Это потому, что я использую PostgreSQL (Npgsql.EntityFrameworkCore.PostgreSQL v3.1.0)?


person WΩLLE - ˈvɔlə    schedule 03.02.2020    source источник
comment
Возможно, SomeTable - это представление?   -  person jarlh    schedule 03.02.2020
comment
Это может быть связано с тем, что вы используете Npgsql, поскольку он официально не поддерживается Microsoft, но является сторонним лицом Поставщик данных. И поскольку поставщик данных отвечает за фактическое преобразование выражения LINQ во что-либо (PSQL для вас), возможно, поставщик данных Npsql не поддерживает эти функции (пока). То, что вы слышали, может относиться исключительно к поставщикам данных, разработанным MS.   -  person MindSwipe    schedule 03.02.2020
comment
Вы можете использовать подготовленные операторы (которые поддерживаются Npgsql) для написания SQL-запроса вручную. а затем выполните его, таким образом вы можете убедиться, что ваш SQL-запрос оптимизирован так, как вы хотите   -  person MindSwipe    schedule 03.02.2020
comment
Хм, это указывает на оценку клиента, поэтому, похоже, не выводится из EF Core 3.0+. Оценка клиента удалена, и вы должны получить исключение во время выполнения, если EF Core не сможет перевести ваш GroupBy запрос или его часть. Какой тип x.Foobar кстати?   -  person Ivan Stoev    schedule 03.02.2020
comment
@jarlh SomeTable определенно является таблицей.   -  person WΩLLE - ˈvɔlə    schedule 03.02.2020
comment
@MindSwipe Спасибо, но я уже нашел решение моей конкретной проблемы, создав представление, которое выполняет группировку и другие вещи. И проблема могла быть в проблеме Npsql, я думаю, вы правы. Но я подумал, может, я просто сделал простую ошибку.   -  person WΩLLE - ˈvɔlə    schedule 03.02.2020
comment
@IvanStoev Да, именно поэтому мне было интересно, что из-за проверки клиента не было создано никаких исключений. x.Foobar - 32-битное целое число   -  person WΩLLE - ˈvɔlə    schedule 03.02.2020
comment
Я бы буквально не называл себя оптимизацией. Это базовый SQL, который EntityFramework выполнил более 10 лет назад. Ef Core начинался с абсолютного минимума функциональности и постепенно приближается к тому, чтобы его можно было использовать.   -  person TomTom    schedule 03.02.2020


Ответы (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, которые дают вам такой результат? Хотя посмотрите на разницу между двумя выпусками npgsql Я не вижу ничего, связанного с проблемой, но я предлагаю вам попробовать обновить все пакеты, связанные с EF, до 3.1.1 и повторить тест.

person timur    schedule 03.02.2020
comment
Я попробую. Большое спасибо за расследование! - person WΩLLE - ˈvɔlə; 03.02.2020