关于AsNoTrackingWithIdentityResolution()实例解析失效及后台追踪的技术问询
关于EF Core
AsNoTrackingWithIdentityResolution()的疑问解答 问题复现代码
// 初始时表为空 Employee emp1 = new Employee() { FirstName = "name1", LastName = "name2", Salary = 11111 }; context.Add(emp1); context.SaveChanges(); Employee emp2 = (from e in context.Employees where e.FirstName == "name1" select e).SingleOrDefault(); // 身份解析生效 Console.WriteLine(emp1 == emp2); // 输出 True Employee emp3 = (from e in context.Employees where e.FirstName == "name1" select e).AsNoTrackingWithIdentityResolution().SingleOrDefault(); // 预期因使用AsNoTrackingWithIdentityResolution()返回True Console.WriteLine(emp1 == emp3); // 实际输出 False Employee emp4 = (from e in context.Employees where e.FirstName == "name1" select e).AsNoTrackingWithIdentityResolution().SingleOrDefault(); // 同样未触发身份解析 Console.WriteLine(emp4 == emp3); // 输出 False
使用版本:EF Core 8.0.2
仅当两个实例被上下文变更追踪器追踪时,引用比较才返回True,其他场景下实例未被解析为同一个。
疑问解答
1. 为何使用AsNoTrackingWithIdentityResolution()查询时,同一数据行的实例未被解析为同一个?
这是对该方法功能范围的误解:
- 该方法的身份解析仅作用于单次查询的执行过程中:它会为当前查询创建一个临时的独立追踪器,确保同一次查询内(比如查询包含关联数据、同一实体多次返回时),同一数据行只会生成一个实体实例。
- 你代码中的
emp3和emp4是两次完全独立的查询,每次查询都会生成自己的临时追踪器,查询结束后该追踪器就会被销毁,因此两次查询生成的实例完全独立,引用比较自然为False。 - 另外,
emp1是被上下文的常规变更追踪器管理的实例,而AsNoTrackingWithIdentityResolution()的临时追踪器与上下文追踪器完全隔离,两者不会共享实例,所以emp1 == emp3必然为False。
2. 该方法的目标是停止追踪,为何后台仍运行独立追踪器?
这里的“停止追踪”指的是不会让上下文的长期变更追踪器持有实体实例,不会跟踪实体后续变更,也不会在上下文生命周期内保留实例引用。而临时独立追踪器是实现身份解析的必要手段:
- 身份解析需要一个容器来记录已创建的实体实例,避免同一数据行生成重复实例,这个临时追踪器就是干这个的。
- 这个临时追踪器是查询专属的,查询执行完毕后会立即被回收,不会像上下文追踪器那样长期存在,因此不会造成持续的内存占用或变更跟踪开销,完全符合“不追踪”的核心目标。
内容的提问来源于stack exchange,提问作者Hossam Zaki
相关产品推荐
相关产品推荐

