EF查询为何仅每个唯一FK对应的首行导航属性为null?
问题分析:EF查询中唯一外键首条数据导航属性为null的原因
问题场景
实体类定义:
public class ShippingInfo{ public int Id { get; set; } public string Invoice { get; set; } public int AssignedUserId { get; set; } public User AssignedUser { get; set; } } public class User{ public int Id { get; set; } public string FullName{ get; set; } public ICollection<ShippingInfo> ShippingInfos{ get; set; } }
- 首次执行带分页的ShippingInfo查询,未Include
AssignedUser导航属性,返回结果正常; - 后续执行不带分页的查询,使用
Include(x => x.AssignedUser)加载导航属性,查询语句:
但返回结果中,每个唯一var query = await _db.Entities.ShippingInfos.Include(x => x.AssignedUser).ToListAsync();AssignedUserId对应的第一条ShippingInfo数据,其AssignedUser为null,同外键的其他数据导航属性正常,示例:{Id = 1, Invoice = "ABCDEF", AssignedUserId = 1, AssignedUser = null} {Id = 3, Invoice = "111222", AssignedUserId = 1, AssignedUser = {User}} {Id = 5, Invoice = "qweq", AssignedUserId = 2, AssignedUser = null} {Id = 6, Invoice = "qwe1", AssignedUserId = 2, AssignedUser = {User}} - 给第二次查询添加
.AsNoTracking()后,问题解决。
原因解释
这是EF Core上下文的**一级缓存(跟踪机制)**导致的:
- 第一次分页查询时,没有Include
AssignedUser,所以被加载到上下文的ShippingInfo实例(比如Id=1、Id=5),其AssignedUser属性为null,且这些实例被上下文跟踪; - 第二次查询时,EF会先检查上下文是否已存在相同主键的
ShippingInfo实例:- 对于已经存在的实例(如Id=1、Id=5),EF会直接复用上下文里的现有实例,不会将本次查询中加载的
AssignedUser赋值给它——因为EF默认不会覆盖已跟踪实例的属性,避免意外修改; - 对于第一次查询没加载到的实例(如Id=3、Id=6),上下文里没有对应实例,EF会创建新实例并赋值
AssignedUser,所以导航属性正常。
- 对于已经存在的实例(如Id=1、Id=5),EF会直接复用上下文里的现有实例,不会将本次查询中加载的
解决方案
可以通过以下方式解决:
- 添加
.AsNoTracking()到第二次查询,让EF不跟踪查询结果,每次创建全新实例:var query = await _db.Entities.ShippingInfos.Include(x => x.AssignedUser).AsNoTracking().ToListAsync(); - 第一次分页查询时,同步Include
AssignedUser导航属性,确保上下文跟踪的实例带有完整数据; - 第二次查询前,清空上下文缓存,强制EF重新加载数据:
_db.ChangeTracker.Clear(); var query = await _db.Entities.ShippingInfos.Include(x => x.AssignedUser).ToListAsync();
内容的提问来源于stack exchange,提问作者Daniel Žeimo
相关产品推荐
相关产品推荐

