跟踪行为决定了 Entity Framework Core 是否将有关实体实例的信息保留在其更改跟踪器中。 如果已跟踪某个实体,则该实体中检测到的任何更改都会在 SaveChanges() 期间永久保存到数据库。 EF Core 还将修复跟踪查询结果中的实体与更改跟踪器中的实体之间的导航属性。
(1)跟踪查询
默认情况下,跟踪返回实体类型的查询。 这表示可以更改这些实体实例,然后通过 SaveChanges() 持久化这些更改。 在以下示例中,将检测到对博客分级所做的更改,并在 SaveChanges() 期间将这些更改永久保存到数据库中。
var blog = context.Blogs.SingleOrDefault(b => b.BlogId == 1);
blog.Rating = 5;
context.SaveChanges();
在跟踪查询中返回结果时,EF Core 将检查上下文中是否已存在实体。 如果 EF Core 找到现有的实体,则返回同样的实例。 EF Core 不会用数据库值覆盖该实体中实体属性的当前值和原始值。 如果未在上下文中找到该实体,EF Core 将创建新的实体实例,并将其附加到上下文。 查询结果不会包含任何已添加到上下文但尚未保存到数据库中的实体。
(2)非跟踪查询
在只读方案中使用结果时,非跟踪查询十分有用。 可以更快速地执行非跟踪查询,因为无需设置更改跟踪信息。 如果不需要更新从数据库中检索到的实体,则应使用非跟踪查询。 可以将单个查询替换为非跟踪查询。 非跟踪查询也会根据数据库中的内容提供结果,但不考虑本地更改或已添加的实体。
var blogs = context.Blogs
.AsNoTracking()
.ToList();
还可以在上下文实例级别更改默认跟踪行为:
context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
var blogs = context.Blogs.ToList();
(3)标识解析
由于跟踪查询使用更改跟踪器,因此 EF Core 将在跟踪查询中执行标识解析。 当具体化实体时,如果 EF Core 已被跟踪,则会从更改跟踪器返回相同的实体实例。 如果结果中多次包含相同的实体,则每次会返回相同的实例。 非跟踪查询不会使用更改跟踪器,也不会执行标识解析。 因此会返回实体的新实例,即使结果中多次包含相同的实体也是如此。 从 EF Core 5.0 开始,可以在同一个查询中结合使用上述两种行为。 也就是说,可以使用非跟踪查询并对结果执行标识解析。 我们添加了另一个运算符 AsNoTrackingWithIdentityResolution(),就像添加 AsNoTracking() 可查询运算符一样。 QueryTrackingBehavior 枚举中也添加了一个关联项。 如果将查询配置为使用标识解析和非跟踪行为,生成查询结果时我们将在后台使用独立的更改追踪器,以便仅将每个实例具体化一次。 此更改追踪器不同于上下文中的更改追踪器,因此上下文不会追踪这些结果。 完全枚举查询后,该更改追踪器将超出范围,并根据需要对其进行垃圾回收。
var blogs = context.Blogs
.AsNoTrackingWithIdentityResolution()
.ToList();