TGViewer
.NET Разработчик .NET Разработчик @netdeveloperdiary · 6.75K subscribers
Post #2386 2.77K
День 1973. #ЗаметкиНаПолях
Особенности Bulk-Операций в EF Core

Когда вы имеете дело с тысячами или даже миллионами записей, эффективность имеет решающее значение. В EF Core 7 представлены два новых метода: ExecuteUpdate и ExecuteDelete (и их асинхронные перегрузки), предназначенные для упрощения Bulk-операций в БД. Однако они обходят трекер изменений EF Core, что может привести к неожиданному поведению, если вы об этом не знаете.

Трекер Изменений
Когда вы загружаете объекты из БД с помощью EF Core, трекер изменений начинает их отслеживать. Когда вы обновляете свойства, удаляете объекты или добавляете новые, он записывает эти изменения:
using (var context = new AppDbContext())
{
// Загрузка
var pr = context.Products
.FirstOrDefault(p => p.Id == 1);
// Изменение
pr.Price = 99.99;

// Здесь трекер знает, что pr изменён

// Добавление
var newPr = new Product {
Name = "New Gadget", Price = 129.99 };
context.Products.Add(newPr);

// Удаление
context.Products.Remove(pr);

// Сохранение всех изменений в БД
context.SaveChanges();
}

Когда вы вызываете SaveChanges, EF Core использует трекер изменений, чтобы определить, какие команды SQL следует выполнить. Это гарантирует, что БД будет синхронизирована с вашими изменениями.

Bulk-операции и трекер изменений
Bulk-операции не используют трекер изменений. Это решение может показаться нелогичным, но за ним стоит веская причина: производительность. Непосредственно выполняя инструкции SQL в БД, EF Core устраняет накладные расходы на отслеживание изменений отдельных объектов.
using (var context = new AppDbContext())
{
// Увеличиваем цену всех продуктов в категории электроника на 10%
context.Products
.Where(p => p.Category == "Electronics")
.ExecuteUpdate(
s => s.SetProperty(p => p.Price, p => p.Price * 1.10));
// Все объекты типа Product в памяти по-прежнему будут иметь старые цены
}

В этом примере метод ExecuteUpdate эффективно преобразует операцию в одну инструкцию SQL UPDATE:
UPDATE [p]
SET [p].[Price] = [p].[Price] * 1.10
FROM [Products] as [p];

Но, если вы проверите экземпляры Product, которые EF Core уже загрузил в память, вы обнаружите, что их свойства Price не изменились. Это может показаться удивительным, если вы не знаете, как массовые обновления взаимодействуют с системой отслеживания изменений. Это же применимо и к методу ExecuteDelete.

Перехватчики EF Core также не запускаются для операций ExecuteUpdate и ExecuteDelete. Если нужно отслеживать или изменять Bulk-операции, вы можете создать триггеры в БД на обновление или удаление.

Проблема: поддержание согласованности
Если ExecuteUpdate завершается успешно, изменения сразу фиксируются в БД, т.к. Bulk-операции обходят трекер изменений и не участвуют в обычной транзакции, управляемой SaveChanges. Если впоследствии SaveChanges завершится сбоем, вы окажетесь в несогласованном состоянии. Изменения, внесенные с помощью ExecuteUpdate, уже сохранятся. Любые изменения, сделанные «в памяти», потеряются.

Самый надежный способ обеспечить согласованность — обернуть в транзакцию и ExecuteUpdate, и операции, которые приводят к SaveChanges:
using (var context = new AppDbContext())
using (var transaction = context.Database.BeginTransaction())
{
try
{
context.Products
.Where(p => p.Category == "Electronics")
.ExecuteUpdate(
s => s.SetProperty(p => p.Price, p => p.Price * 1.10));

// … другие изменения сущностей
context.SaveChanges();
transaction.Commit();
}
catch (Exception ex)
{
transaction.Rollback();
}
}

Если SaveChanges завершится неудачно, транзакция будет отменена с отменой изменений, внесенных как ExecuteUpdate, так и любыми другими операциями внутри транзакции. Это сохранит вашу БД в согласованном состоянии.

Источник: https://www.milanjovanovic.tech/blog/what-you-need-to-know-about-ef-core-bulk-updates
  • 👍 23
More from @netdeveloperdiary
  1. Oct 6, 2026🦈 Открытое собеседование на Middle C# | 6 октября, 19:00 МСК Приглашаем на открытое собес…
  2. Oct 6, 2026День 2806. #ЗаметкиНаПолях Типы Коллекций в .NET, Которые Стоит Попробовать. Начало Больши…
  3. Oct 5, 2026День 2805. #ЧтоНовенького #NET11 Аргументы в Выражениях Коллекций в C#15 В C#15 реализован…
  4. Oct 4, 2026День 2804. #ВопросыНаСобеседовании Марк Прайс предложил свой набор из 60 вопросов (как тех…
  5. Oct 3, 2026Post #3360
  6. Oct 3, 2026День 2803. #Оффтоп Чем Заняться, Пока Работают Агенты? У VS Code Есть Ответ Сейчас большую…
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 →