Поделиться через


Отслеживание и No-Tracking запросов

Контроль за поведением управляет тем, будет ли Entity Framework Core хранить сведения об экземпляре сущности в средстве отслеживания изменений. Если сущность отслеживается, все изменения, обнаруженные в сущности, сохраняются в базе данных во время SaveChanges. EF Core также исправляет свойства навигации между сущностями в результате запроса отслеживания и сущностями, которые находятся в отслеживании изменений.

Замечание

Типы сущностей без ключей никогда не отслеживаются. Когда в этой статье упоминаются типы сущностей, он ссылается на типы сущностей, которые имеют определенный ключ.

Подсказка

Вы можете скачать используемый в этой статье пример из репозитория GitHub.

Отслеживание запросов

По умолчанию запросы, возвращающие типы сущностей, отслеживаются. Запрос отслеживания означает, что любые изменения экземпляров сущностей сохраняются SaveChanges. В следующем примере изменение рейтинга блогов обнаруживается и сохраняется в базе данных во время SaveChanges:

var blog = await context.Blogs.SingleOrDefaultAsync(b => b.BlogId == 1);
blog.Rating = 5;
await context.SaveChangesAsync();

Когда результаты возвращаются в запросе на отслеживание, EF Core проверяет, находится ли сущность уже в контексте. Если EF Core находит существующую сущность, возвращается тот же экземпляр, который может использовать меньше памяти и быть быстрее, чем запрос без отслеживания. EF Core не перезаписывает текущие и исходные значения свойств сущности в записи со значениями базы данных. Если сущность не найдена в контексте, EF Core создает новый экземпляр сущности и присоединяет его к контексту. Результаты запроса не содержат сущности, добавляемой в контекст, но еще не сохраненной в базе данных.

Запросы без отслеживания

Запросы без отслеживания полезны, если результаты используются в сценарии только для чтения. Обычно они быстрее выполняются, так как нет необходимости настраивать сведения об отслеживании изменений. Если сущности, полученные из базы данных, не нужно обновлять, следует использовать запрос без отслеживания. Отдельный запрос можно задать как без отслеживания. Запрос без отслеживания также дает результаты на основе того, что находится в базе данных без учета локальных изменений или добавленных сущностей.

var blogs = await context.Blogs
    .AsNoTracking()
    .ToListAsync();

Поведение отслеживания по умолчанию можно изменить на уровне экземпляра контекста:

context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;

var blogs = await context.Blogs.ToListAsync();

В следующем разделе объясняется, когда запрос без отслеживания может быть менее эффективным, чем запрос отслеживания.

Определение личности

Поскольку запрос отслеживания использует средство отслеживания изменений, EF Core выполняет разрешение идентичностей. При материализации сущности EF Core возвращает тот же экземпляр сущности из средства отслеживания изменений, если он уже отслеживается. Если результат содержит одну и ту же сущность несколько раз, то для каждого вхождения возвращается один и тот же экземпляр. Запросы без отслеживания:

  • Не используйте средство отслеживания изменений и не выполняйте разрешение идентичности.
  • Возвращает новый экземпляр сущности, даже если одна и та же сущность содержится в результате несколько раз.

Отслеживание и отслеживание без отслеживания можно объединить в одном запросе. То есть у вас может быть запрос без отслеживания, который выполняет разрешение идентичностей в результатах запроса. Как и оператор запросов AsNoTracking, мы добавили еще один оператор AsNoTrackingWithIdentityResolution<TEntity>(IQueryable<TEntity>). В перечисление также добавлена связанная запись QueryTrackingBehavior. Если запрос на использование идентификации настроен без отслеживания, в фоновом режиме используется автономный трекер изменений при создании результатов запроса, чтобы каждый экземпляр материализовался только один раз. Так как этот средство отслеживания изменений отличается от одного из них в контексте, результаты не отслеживаются контекстом. После полного перечисления запроса трекер изменений выходит из области видимости и подвергается сборке мусора по мере необходимости.

var blogs = await context.Blogs
    .AsNoTrackingWithIdentityResolution()
    .ToListAsync();

Настройка поведения отслеживания по умолчанию

Если вы часто изменяете поведение отслеживания для многих запросов, возможно, вам лучше изменить значение по умолчанию:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
    optionsBuilder
        .UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=EFQuerying.Tracking;Trusted_Connection=True;ConnectRetryCount=0")
        .UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking);
}

Это делает все ваши запросы неотслеживаемыми по умолчанию. Вы по-прежнему можете добавить AsTracking для отслеживания конкретных запросов.

Мониторинг и пользовательские прогнозы

Даже если тип результата запроса не является типом сущности, EF Core по-прежнему отслеживает типы сущностей, содержащиеся в результате по умолчанию. В следующем запросе, который возвращает анонимный тип, экземпляры Blog в результирующем наборе будут отслеживаться.

var blog = context.Blogs
    .Select(
        b =>
            new { Blog = b, PostCount = b.Posts.Count() });

Если результирующий набор содержит типы сущностей, исходящие из композиции LINQ, EF Core будет отслеживать их.

var blog = context.Blogs
    .Select(
        b =>
            new { Blog = b, Post = b.Posts.OrderBy(p => p.Rating).LastOrDefault() });

Если результирующий набор не содержит типов сущностей, отслеживание не выполняется. В следующем запросе мы возвращаем анонимный тип с некоторыми значениями из сущности (но без экземпляров фактического типа сущности). Отслеживаемые сущности не выходят из запроса.

var blog = context.Blogs
    .Select(
        b =>
            new { Id = b.BlogId, b.Url });

EF Core поддерживает клиентскую оценку в проекции верхнего уровня. Если EF Core материализует экземпляр сущности для оценки клиента, он будет отслеживаться. Здесь, так как мы передаваем blog сущности в клиентский метод StandardizeURL, EF Core также отслеживает экземпляры блога.

var blogs = await context.Blogs
    .OrderByDescending(blog => blog.Rating)
    .Select(
        blog => new { Id = blog.BlogId, Url = StandardizeUrl(blog) })
    .ToListAsync();
public static string StandardizeUrl(Blog blog)
{
    var url = blog.Url.ToLower();

    if (!url.StartsWith("http://"))
    {
        url = string.Concat("http://", url);
    }

    return url;
}

EF Core не отслеживает экземпляры сущностей без ключей, содержащиеся в результате. Но EF Core отслеживает все прочие экземпляры типов сущностей с ключом, следуя вышеупомянутым правилам.

Предыдущие версии

До версии 3.0 EF Core имел некоторые различия в том, как осуществлялось отслеживание. Важные различия приведены ниже.

  • Как описано на странице оценки клиента и сервера , EF Core поддерживает оценку клиента в любой части запроса до версии 3.0. Оценка клиента вызвала материализацию сущностей, которые не были частью результата. Поэтому EF Core проанализировал результат, чтобы определить, что нужно отслеживать. Эта конструкция имеет определенные отличия следующим образом:

    • Оценка клиента в проекции, которая вызвала материализацию, но не вернула материализованный экземпляр сущности, не отслеживалась. В следующем примере не отслеживались blog сущности.

      var blogs = await context.Blogs
          .OrderByDescending(blog => blog.Rating)
          .Select(
              blog => new { Id = blog.BlogId, Url = StandardizeUrl(blog) })
          .ToListAsync();
      
    • EF Core не отслеживал объекты, выходящие из композиции LINQ в некоторых случаях. В следующем примере не отслеживалось Post.

      var blog = context.Blogs
          .Select(
              b =>
                  new { Blog = b, Post = b.Posts.OrderBy(p => p.Rating).LastOrDefault() });
      
  • Всякий раз, когда результаты запроса содержали типы сущностей без ключа, весь запрос становился без отслеживания. Это означает, что типы сущностей с ключами, которые есть в результате, также не отслеживались.

  • EF Core раньше выполнял разрешение идентичности в неотслеживаемых запросах. Она использовала слабые ссылки для отслеживания сущностей, которые уже были возвращены. Таким образом, если результирующий набор содержал одну и ту же сущность несколько раз, вы получите один и тот же экземпляр для каждого вхождения. Хотя, если предыдущий результат с тем же идентификатором вышел из области видимости и был удалён сборщиком мусора, EF Core возвращал новый экземпляр.