跟踪 VS 不跟踪

跟踪行为控制着 Entity Framework Core 是否会在其变更跟踪器里维持实体实例的信息。如果实体是被跟踪的,任何检测到的该实体的变更都将在 SaveChanges() 时持久化到数据库中。Entity Framework Core 还会对已跟踪的、之前已加载到 DbContext 实例中的查询和实体进行相互的导航属性装配。

提示

你可以在 GitHub 上查阅当前文章涉及的代码样例

跟踪查询

默认情况下,返回实体类型实例的查询是被跟踪的。这意味着你可以对这些实体进行更改,并通过 SaveChanges() 将这些变更持久化到数据库中。

在以下代码样例中,对 blog.Rating 的变更会在 SaveChanges() 时被检测并保存到数据库中。

  1. using (var context = new BloggingContext())
  2. {
  3. var blog = context.Blogs.SingleOrDefault(b => b.BlogId == 1);
  4. blog.Rating = 5;
  5. context.SaveChanges();
  6. }

不跟踪查询

在查询结果为只读的场景下,不跟踪查询是很有用的。由于无需建立变更跟踪信息,查询会执行得更快。

  1. using (var context = new BloggingContext())
  2. {
  3. var blogs = context.Blogs
  4. .AsNoTracking()
  5. .ToList();
  6. }

还可以更改上下文级别上的默认跟踪行为。

  1. using (var context = new BloggingContext())
  2. {
  3. context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
  4. var blogs = context.Blogs.ToList();
  5. }

注意

不跟踪查询仍然会执行标识识别。如果结果集包含多个相同的实体,那么它们都将返回该实体类型的同一个实例。无论如何,弱引用会被用来保持对已返回实体的跟踪。如果之前具有同一标识的结果超出了作用范围,GC 会对其进行回收,你可能会获得新的实体实例。更多信息请查看 查询的原理

跟踪和推测

即便查询的结果类型不是一个实体类型,只要结果包含了实体类型,默认情况下它们还是被跟踪的。以下查询返回的是匿名类型数据,结果集中引用的 Blog 实例仍然是被跟踪的。

  1. using (var context = new BloggingContext())
  2. {
  3. var blog = context.Blogs
  4. .Select(b =>
  5. new
  6. {
  7. Blog = b,
  8. Posts = b.Posts.Count()
  9. });
  10. }

如果结果集不包含任何实体类型,就不会执行任何跟踪。以下查询返回的是匿名类型数据,并且匿名类型实例只使用了实体上的一些值(没有引用实际的实体类型实例),EF Core 不会执行任何跟踪。

  1. using (var context = new BloggingContext())
  2. {
  3. var blog = context.Blogs
  4. .Select(b =>
  5. new
  6. {
  7. Id = b.BlogId,
  8. Url = b.Url
  9. });
  10. }