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

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

Примечание.

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

Совет

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

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

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

var blog = context.Blogs.SingleOrDefault(b => b.BlogId == 1);
blog.Rating = 5;
context.SaveChanges();

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

Отключение отслеживания запросов

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

var blogs = context.Blogs
    .AsNoTracking()
    .ToList();

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

context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;

var blogs = context.Blogs.ToList();

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

Разрешение идентификатора

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

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

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

var blogs = context.Blogs
    .AsNoTrackingWithIdentityResolution()
    .ToList();

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

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

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
    optionsBuilder
        .UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=EFQuerying.Tracking;Trusted_Connection=True")
        .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 = context.Blogs
    .OrderByDescending(blog => blog.Rating)
    .Select(
        blog => new { Id = blog.BlogId, Url = StandardizeUrl(blog) })
    .ToList();
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 были некоторые различия в способах отслеживания. Различия заключаются в следующем.

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

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

      var blogs = context.Blogs
          .OrderByDescending(blog => blog.Rating)
          .Select(
              blog => new { Id = blog.BlogId, Url = StandardizeUrl(blog) })
          .ToList();
      
    • В некоторых случаях 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 возвращает новый экземпляр.