Что такое Change Tracking (отслеживание изменений) в Entity Framework?

«Что такое Change Tracking (отслеживание изменений) в Entity Framework?» — вопрос из категории Entity Framework, который задают на 25% собеседований C# Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Change Tracking — это фундаментальный механизм в Entity Framework Core, который автоматически отслеживает изменения, происходящие с сущностями, привязанными к экземпляру DbContext. При вызове SaveChanges() EF генерирует и выполняет необходимые SQL-команды (INSERT, UPDATE, DELETE) на основе этих отслеженных изменений.

Как это работает на практике:

  1. Загрузка и отслеживание: Когда сущность загружается через контекст (например, Find(), ToList() без AsNoTracking()), EF начинает отслеживать ее, запоминая исходные значения свойств (снимок состояния).
  2. Обнаружение изменений: При изменении свойств сущности контекст фиксирует это. Вы можете явно проверить изменения через context.ChangeTracker.HasChanges().
  3. Синхронизация с БД: При вызове SaveChanges() EF сравнивает текущие значения с исходными, определяет, какие сущности были добавлены, изменены или удалены, и формирует пакет команд для БД.

Пример:

using (var db = new AppDbContext())
{
    // Сущность загружается и начинает отслеживаться
    var blog = db.Blogs.Find(1);
    // Изменение свойства отслеживается
    blog.Rating = 5;
    // Добавление новой отслеживаемой сущности
    db.Posts.Add(new Post { Title = "New Post" });

    // На этом этапе EF знает, что нужно:
    // - UPDATE для Blogs (изменен Rating)
    // - INSERT для Posts (добавлен новый Post)
    db.SaveChanges(); // Команды отправляются в БД
}

Ключевые моменты и управление:

  • Производительность: Отслеживание требует памяти и процессорного времени. Для сценариев "только чтение" используйте AsNoTracking() или AsNoTrackingWithIdentityResolution().
  • Отслеживание по умолчанию: Работает для сущностей, полученных из контекста, а также для вновь добавленных (Add, AddRange).
  • Ручное управление состоянием: Вы можете явно указать состояние сущности через db.Entry(entity).State = EntityState.Modified (или Added, Deleted, Unchanged, Detached). Это полезно при работе с отключенными сущностями (например, из веб-запроса).
  • Detached: Состояние Detached означает, что контекст больше не отслеживает сущность.