TGViewer
.NET Разработчик .NET Разработчик @netdeveloperdiary · 6.75K subscribers
Post #1937 1.63K
День 1591. #ЗаметкиНаПолях
Повышаем Производительность с Помощью Разделения Запросов в EF Core
Некоторые сложные многотабличные запросы могут выполняться слишком долго в зависимости от количества данных в таблицах.

Допустим, у нас есть таблица заказов Orders. Заказ может иметь одну или несколько позиций LineItems. Кроме того, для позиций заказов есть таблица, содержащая допустимые измерения — LineItemDimensions. Вот запрос, который нам нужен:
dbContext
.Orders
.Include(o => o.LineItems)
.ThenInclude(li => li.Dimensions)
.First(o => o.Id == orderId);

EF Core преобразует это в следующий SQL-запрос:
SELECT o.*, li.*, d.*
FROM Orders o
LEFT JOIN LineItems li ON li.OrderId = o.Id
LEFT JOIN LineItemDimensions d ON d.LineItemId = li.Id
WHERE o.Id = @orderId
ORDER BY o.Id, li.Id, d.Id;

В большинстве случаев этот запрос будет выполняться нормально. Однако здесь мы сталкиваемся с проблемой декартова взрыва - ситуацией, когда на выбор N заказов с M позициями и K измерениями база данных возвращает N*M*K строк.

В EF Core 5.0 появилась новая функция разделения запросов. Она позволяет указать, что данный LINQ-запрос должен быть разделён на несколько SQL-запросов. Всё, что нужно сделать, это вызвать метод AsSplitQuery:
dbContext
.Orders
.Include(o => o.LineItems)
.ThenInclude(li => li.Dimensions)
.AsSplitQuery()
.First(o => o.Id == orderId);

В этом случае EF Core сгенерирует следующие SQL-запросы:
SELECT o.*
FROM Orders o
WHERE o.Id = @orderId;

SELECT li.*
FROM LineItems li
JOIN Orders o ON li.OrderId = o.Id
WHERE o.Id = @orderId;

SELECT d.*
FROM LineItemDimensions d
JOIN LineItems li ON d.LineItemId = li.Id
JOIN Orders o ON li.OrderId = o.Id
WHERE o.Id = @orderId;
Обратите внимание, что для каждого оператора Include у нас есть отдельный запрос. Преимущество здесь в том, что мы не выбираем лишние данные, как это было в предыдущем случае.

Вы можете включить разделение запросов на уровне контекста, вызвав метод UseQuerySplittingBehavior:
services.AddDbContext<ApplicationDbContext>(opt =>
opt.UseSqlServer(
"CONNECTION_STRING",
o => o.UseQuerySplittingBehavior(
QuerySplittingBehavior.SplitQuery)));

Это приведет к тому, что все запросы, создаваемые EF Core, будут разделёнными запросами. Чтобы вернуться к единому SQL-запросу в этом случае, в LINQ-запрос нужно будет добавить AsSingleQuery().

Особенности
- Не существует гарантии согласованности для нескольких SQL-запросов. Вы можете столкнуться с проблемой, если одновременно с запросом данных выполняется параллельное обновление. Чтобы смягчить это, можно обернуть запросы в транзакцию, но это также может привести к проблемам с производительностью.
- Каждый запрос требует отдельного обращения к БД по сети. Это может снизить производительность, если задержка для базы высока.

Источник: https://www.milanjovanovic.tech/blog/entity-framework-query-splitting
  • 👍 21
  • 👎 1
More from @netdeveloperdiary
  1. Oct 7, 2026День 2807. #ЗаметкиНаПолях Типы Коллекций в .NET, Которые Стоит Попробовать. Окончание Нач…
  2. Oct 6, 2026🦈 Открытое собеседование на Middle C# | 6 октября, 19:00 МСК Приглашаем на открытое собес…
  3. Oct 6, 2026День 2806. #ЗаметкиНаПолях Типы Коллекций в .NET, Которые Стоит Попробовать. Начало Больши…
  4. Oct 5, 2026День 2805. #ЧтоНовенького #NET11 Аргументы в Выражениях Коллекций в C#15 В C#15 реализован…
  5. Oct 4, 2026День 2804. #ВопросыНаСобеседовании Марк Прайс предложил свой набор из 60 вопросов (как тех…
  6. Oct 3, 2026Post #3360
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →