Я изучал скомпилированные запросы с использованием Entity Framework Core.Я на текущей последней стабильной версии 2.2.2.Я читал эту статью (https://docs.microsoft.com/en-us/dotnet/framework/data/adonet/ef/language-reference/compiled-queries-linq-to-entities), чтобы понять скомпилированные запросы, и пытаюсь понять, является ли это ошибкой в EF Core или просто тем, что они еще не исправили. Я понимаю, что статья написана для EF6, но ожидал, что скомпилированные запросы будут работать одинаково и не смогут найти ничего противоположного.
Вот мои настройки DbContext и простая структура для параметров подкачки:
public class BuggyDbContext : DbContext
{
public DbSet<User> Users { get; set; }
}
public struct PagingOptions
{
public int Skip;
public int Take;
}
[Table("User")]
public class User
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int UserId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
Вот мои скомпилированные запросы. Первый выбирает «страницу» пользователей на основе параметра структуры (очень похоже на пример из статьи). Второй выполняет то же самое, но принимает «пропустить» и «взять» в качестве отдельных параметровбазовый тип int32.
var badQuery = EF.CompileQuery<BuggyDbContext, PagingOptions, IEnumerable<User>>((context, paging) =>
context.Users
.OrderBy(u => u.LastName)
.Skip(paging.Skip)
.Take(paging.Take));
var goodQuery = EF.CompileQuery<BuggyDbContext, int,int, IEnumerable<User>>((context, skip, take) =>
context.Users
.OrderBy(u => u.LastName)
.Skip(skip)
.Take(take));
А вот пример использования для демонстрации проблемы:
using (var db = new BuggyDbContext())
{
var pagingOptions = new PagingOptions {
Skip = 0,
Take = 25
};
var firstPage = badQuery.Invoke(db, pagingOptions).ToList();
var alternateFirstPage = goodQuery.Invoke(db, pagingOptions.Skip, pagingOptions.Take).ToList();
}
Когда работает goodQuery , все работает как положено.в журналах будет показано следующее сгенерированное значение SQL:
SELECT [u].[UserId], [u].[FirstName], [u].[LastName]
FROM [User] AS [u]
ORDER BY [u].[LastName]
OFFSET @__skip ROWS FETCH NEXT @__take ROWS ONLY
Однако, когда запускается badQuery , выберите ВСЕ иззаписи, а затем оценивает пропуск в памяти, что может привести к ужасной производительности.
SELECT [u].[UserId], [u].[FirstName], [u].[LastName]
FROM [User] AS [u]
ORDER BY [u].[LastName]
warn: Microsoft.EntityFrameworkCore.Query[20500]
=> Microsoft.EntityFrameworkCore.Query.RelationalQueryModelVisitor
The LINQ expression 'Skip(__paging.Skip)' could not be translated
and will be evaluated locally.
warn: Microsoft.EntityFrameworkCore.Query[20500]
=> Microsoft.EntityFrameworkCore.Query.RelationalQueryModelVisitor
The LINQ expression 'Take(__paging.Take)' could not be translated
and will be evaluated locally.
Я бы предпочел использовать сложный тип (структура ссылки или значения, все равно) в качестве параметровк скомпилированным запросам по двум очень важным причинам:
- Лямбда-функции имеют максимальное количество входных параметров.Если бы у меня был запрос с какой-то сложной фильтрацией, сортировкой и группировкой, который требовал нескольких входов, я был бы вынужден пойти по другому пути.
- Входные параметры намного понятнее разработчику, который вызываетзапрос.Даже в этом примере разработчик, вызывающий запрос, начнет вводить query.Invoke, а затем будет смотреть на 2 безымянных целочисленных аргумента в intellisense.Единственный способ узнать, что они имеют в виду, это посмотреть на запрос.Изменения в запросе были бы чрезвычайно опасны, если бы входные параметры изменили порядок или значение.
Дорожная карта EF Core 3.0 (https://docs.microsoft.com/en-us/ef/core/what-is-new/roadmap) говорит, что они работают над своей стратегией LINQ Query в целом (чтобы избежать таких ужасно выполняющихся запросов или, по крайней мере, дать вам знать об этом до выполнения иличто-то перехватывает предупреждение в ваших журналах), но я ожидал, что параметр struct будет работать.
У кого-нибудь есть понимание, если я что-то делаю неправильно или это что-то, что находится в разработке?это тоже ошибка?