Ответ
DbContext использует механизм Change Tracking (отслеживание изменений), который работает автоматически для сущностей, загруженных в его контекст.
Как это работает:
-
Загрузка и "отслеживание": Когда вы загружаете сущность (через
Find(),FirstOrDefault(),ToList()безAsNoTracking()), DbContext начинает её отслеживать. Он сохраняет оригинальные значения свойств сущности в своем внутреннем состоянии (EntityEntry). -
Обнаружение изменений: При изменении свойств отслеживаемой сущности, DbContext сравнивает текущие значения с сохраненными оригинальными. Это сравнение может происходить автоматически (при вызове
SaveChanges()) или вручную черезChangeTracker.DetectChanges(). -
Сохранение (
SaveChanges()): Этот метод:- Определяет все отслеживаемые сущности с изменениями.
- Присваивает каждой из них состояние (
Added,Modified,Deleted,Unchanged). - Генерирует и выполняет необходимые SQL-команды (
INSERT,UPDATE,DELETE) в одной транзакции.
Пример:
using var context = new AppDbContext();
// 1. Загрузка и начало отслеживания
var blog = context.Blogs.Find(1); // Состояние: Unchanged
// 2. Внесение изменений
blog.Rating = 5; // Контекст фиксирует изменение
blog.Url = "https://newurl.com";
// Состояние теперь: Modified (но до вызова SaveChanges изменения не в БД)
// 3. Сохранение
var savedCount = context.SaveChanges(); // Генерирует SQL: UPDATE Blogs SET Rating = 5, Url = '...' WHERE Id = 1
Console.WriteLine($"Сохранено {savedCount} записей.");
Важные детали и best practices:
- Отключение отслеживания: Используйте
AsNoTracking()в запросах, предназначенных только для чтения, чтобы повысить производительность.var readOnlyList = await context.Blogs.AsNoTracking().ToListAsync(); - Явное указание состояния: Если сущность не была загружена через контекст (например, получена из сети), нужно явно указать её состояние.
var detachedBlog = new Blog { Id = 1, Rating = 5 }; context.Entry(detachedBlog).State = EntityState.Modified; // Будет выполнен UPDATE await context.SaveChangesAsync(); - Отслеживание графов объектов: Изменения в связанных сущностях (например, в коллекции
PostsвнутриBlog) также отслеживаются и сохраняются. - Производительность: Для массовых операций (вставка/обновление тысяч строк) стандартный
SaveChanges()неэффективен. ИспользуйтеAddRange()/RemoveRange()или специализированные библиотеки вроде EF Core Bulk Extensions.