Ответ
EF Core отслеживает изменения всех сущностей, полученных в контексте (с AsTracking()). При вызове SaveChanges() он фиксирует все накопленные изменения в базе данных внутри транзакции.
Что именно происходит:
- Анализ ChangeTracker: EF Core проверяет
DbContext.ChangeTrackerна наличие сущностей в состоянияхAdded,Modified, илиDeleted. - Генерация SQL: Для каждой измененной сущности генерируется соответствующий SQL-команда (
INSERT,UPDATE,DELETE). - Выполнение в транзакции: Все команды выполняются как одна транзакция (если не используется явная транзакция).
- Сохранение или откат: Если все команды выполнены успешно, транзакция коммитится, и состояния сущностей меняются на
Unchanged. При ошибке транзакция откатывается, а исключение пробрасывается выше.
Практический пример:
using var db = new AppDbContext();
// Сущность теперь отслеживается контекстом
var blog = await db.Blogs.FirstAsync(b => b.Id == 1);
blog.Rating = 5; // Состояние меняется на Modified
blog.Url = "https://newurl.com";
// Добавляем новую сущность
db.Blogs.Add(new Blog { Url = "https://example.com" }); // Состояние Added
// Удаляем сущность
db.Remove(await db.Blogs.FirstAsync(b => b.Id == 99)); // Состояние Deleted
// Все три операции (UPDATE, INSERT, DELETE) будут выполнены здесь
int affectedRows = await db.SaveChangesAsync();
Console.WriteLine($"Затронуто строк: {affectedRows}");
Ключевые настройки и best practices:
SaveChangesAsync(): Всегда предпочтительнее синхронного варианта для не блокирующего IO.- Пакетная обработка (Batching): EF Core может объединять несколько команд в один пакет (roundtrip) для повышения производительности.
- Оптимистическая блокировка: Используйте свойство с атрибутом
[ConcurrencyCheck]илиTimestamp, чтобы предотвратить конфликты параллельного обновления. - Отключение автоматического отслеживания: В сценариях только для чтения используйте
AsNoTracking()для повышения производительности.