Как отключить отслеживание сущности в Entity Framework?

Ответ

В Entity Framework Core есть несколько способов отключить механизм отслеживания изменений (change tracking) для сущностей, что полезно для операций только для чтения и повышения производительности.

Основные методы:

  1. AsNoTracking() — отключает отслеживание для результатов конкретного запроса.

    var users = context.Users.AsNoTracking().ToList();
  2. AsNoTrackingWithIdentityResolution() — аналогичен AsNoTracking, но обеспечивает разрешение идентичности: если в результате запроса встречается одна и та же сущность (по ключу) несколько раз, будет возвращен один и тот же экземпляр объекта. Это полезно для предотвращения дублирования в графах объектов.

  3. Глобальная настройка контекста — можно установить поведение по умолчанию для всех запросов данного контекста.

    services.AddDbContext<AppDbContext>(options =>
        options.UseSqlServer(connectionString)
               .UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking));

    После этого для запросов, требующих отслеживания, нужно явно использовать .AsTracking().

  4. Отсоединение сущности (Detach) — если сущность уже загружена с отслеживанием, её можно отсоединить.

    context.Entry(entity).State = EntityState.Detached;

Когда это нужно:

  • При выполнении запросов только для чтения (отчеты, экспорт данных).
  • При маппинге результатов запроса в DTO или ViewModel.
  • В сценариях с кратковременным контекстом (например, в веб-приложениях), когда изменения не планируется сохранять.

Важно: При отключенном отслеживании любые изменения, внесенные в полученные объекты, не будут автоматически сохранены в базу данных при вызове SaveChanges().

Ответ 18+ 🔞

Слушай, а вот эта ваша штука с отслеживанием изменений в Entity Framework — она же, блядь, иногда так жрёт ресурсы, что мама не горюй! Особенно если ты просто данные читаешь, как на параше газетку. Ну, типа отчёты там, или что-то выгружаешь. А он там, сука, за каждым объектом следит, в кэш пихает, состояние меняет — в общем, овердохуища телодвижений.

Так вот, чтобы эту поебень отключить, есть несколько способов. Выбирай, как тебе удобнее.

Первое — точечно, прямо в запросе. Самый частый случай.

var users = context.Users.AsNoTracking().ToList();

Всё, после AsNoTracking() эти юзеры — как чужие трусы. Меняй их сколько влезет, контекст про это нихуя не узнает и в базу ничего не отправит. Производительность сразу подскакивает, потому что EF не строит свой внутренний цирк с конями вокруг каждого экземпляра.

Второе — почти то же самое, но с приколом. Бывает, в результатах запроса одна и та же хрень по ключу встречается несколько раз (ну, в связанных данных). Обычный AsNoTracking() для каждой встречи создаст новый объект в памяти — получишь клонов, как в "Звёздных войнах". Чтобы этого не было, есть AsNoTrackingWithIdentityResolution(). Он такой: "Ага, юзер с ID=1? Я его уже видел, вот, держи ссылку на тот же самый объект". Графы объектов не дублируются, красота.

Третье — глобально, на весь контекст. Если у тебя в проекте 90% запросов — чтение, можно не писать AsNoTracking() в каждом методе, а сказать контексту раз и навсегда: "Не отслеживай нихуя по умолчанию".

services.AddDbContext<AppDbContext>(options =>
    options.UseSqlServer(connectionString)
           .UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking));

Теперь все запросы будут без отслеживания. А если вдруг в оставшихся 10% нужно что-то изменить и сохранить — явно юзай .AsTracking(). Да, придётся помнить об этом, зато везде, где можно, получишь профит.

Четвёртое — экстренное отсоединение. Бывает, сущность уже в контексте болтается, отслеживается, а тебе надо, чтобы отстала. Ну, например, для каких-то мануальных манипуляций. Тогда делай так:

context.Entry(entity).State = EntityState.Detached;

Всё, контекст про неё забыл. Как будто и не было.

Когда это всё, блядь, нужно?

  • Когда только читаешь (отчёты, дашборды, экспорт в Excel). Это главный случай.
  • Когда маппишь результаты в DTO или ViewModel. Зачем отслеживать то, во что ты просто копируешь данные?
  • В короткоживущих контекстах, типа веб-запроса, где ты чаще всего ничего не меняешь, а просто показываешь данные. Это вообще best practice, между прочим.

И главное предупреждение, нахуй! Если ты взял данные через AsNoTracking(), потом их там поредактировал и вызвал SaveChanges() — нихуя не сохранится! Контекст про эти изменения просто не в курсе. Он за ними не следил. Так что если нужно менять — либо бери с отслеживанием, либо прикрепляй (Attach) и меняй состояние вручную. Не напоминай — сам знаешь.