EF Core访问关联表时Colleague的StaffPosition导航属性为空问题
我尝试在EF Core中访问关联表,以便在View中展示对应列。我认为是遗漏了某个基础知识点,但始终无法解决问题。
简化后的场景:我有3个模型Branch、Colleague、StaffPosition,每个Colleague关联1个Branch,且仅可拥有1个StaffPosition,所有模型均使用Code First创建:
Branch 模型
public class Branch { [Display(Name ="Branch ID")] public int BranchNumber { get; set; } [Display(Name ="Branch Name")] public string BranchName { get; set; } [Display(Name ="Contractor Code")] public int ContractorCode { get; set; } [Display(Name ="Addresss")] public string AddressOne { get; set; } // 为简化内容省略其余代码 public ICollection<BranchHour> BranchHours { get; set; } public ICollection<Colleague> Colleagues { get; set; } }
Colleague 模型
public class Colleague { [Display(Name ="ID")] public int ColleagueID { get; set; } public int BranchID { get; set; } public int StaffPositionID { get; set; } [Display(Name ="Payroll Number")] public string PayrollNumber { get; set; } // 为简化内容省略其余代码 public ICollection<Shift> Shifts { get; set; } [ForeignKey("StaffPositionID")] public StaffPosition StaffPosition { get; set; } [ForeignKey("BranchID")] public Branch Branch { get; set; } }
StaffPosition 模型
public class StaffPosition { public int StaffPositionID { get; set; } public string PositionName { get; set; } public ICollection<Colleague> Colleagues { get; set; } }
视图模型定义
public class BranchShiftData { public IEnumerable<Branch> Branches { get; set; } public IEnumerable<Colleague> Colleagues { get; set; } public IEnumerable<Shift> Shifts { get; set; } public IEnumerable<StaffPosition> StaffPos { get; set; } }
最初我没有在视图模型中加入Shifts和StaffPosition,但发现无法通过Colleague访问Shifts数据,因此补充了这两个属性。
页面后台OnGet方法代码
BranchData = new BranchShiftData(); BranchData.Branches = await _context.Branches .Include(i => i.Colleagues) .OrderBy(i => i.BranchNumber) .ToListAsync(); if (id != null) { BranchID = id.Value; Branch branch = BranchData.Branches .Where(i => i.ContractorCode == id.Value).Single(); BranchData.Colleagues = branch.Colleagues .OrderBy(i => i.FullName); } if (colleagueID != null) { ColleagueID = colleagueID.Value; var selectedColleague = BranchData.Colleagues .Where(x => x.ColleagueID == colleagueID).Single(); FullName = selectedColleague.FullName; PayrollNumber = selectedColleague.PayrollNumber; BranchName = selectedColleague.Branch.BranchName; ContractHours = selectedColleague.ContractHours; PositionName = selectedColleague.StaffPosition.PositionName;
我遇到的问题是:可以通过selectedColleague的关联属性获取BranchName,但无法用同样方式访问StaffPosition表的内容。在Web应用中选中单个Colleague时,程序会在为PositionName赋值的行抛出如下错误:
NullReferenceException: Object reference not set to an instance of an object.
经排查变量栈可知,selectedColleague的StaffPosition属性为null。
我无法理解的是:selectedColleague的BranchID为5558(取值正确),对应的Branch对象也已正确加载、可查看5558号Branch的所有信息;但selectedColleague的StaffPositionID取值正确为1,对应的StaffPosition属性却为空。
我的核心问题是:为什么可以通过selectedColleague.Branch.BranchName获取分支名称,却无法通过selectedColleague.StaffPosition.PositionName获取职位名称?
原因分析
你在查询Branch时只通过Include预先加载了Colleagues关联集合,但没有继续加载Colleagues下的StaffPosition导航属性,EF Core默认不会自动加载未显式指定的关联属性,因此StaffPosition属性为null,访问时就会抛出空引用异常。
而Branch属性可以正常访问的原因是:你查询的根对象就是Branch,所有关联的Colleague对应的Branch对象已经被EF Core上下文追踪,所以自动填充了反向导航属性的值。
解决方法
修改你查询Branch的代码,使用ThenInclude方法继续加载Colleague下的StaffPosition导航属性即可:
BranchData.Branches = await _context.Branches .Include(i => i.Colleagues) .ThenInclude(c => c.StaffPosition) // 新增此行加载Colleague关联的StaffPosition // 如果后续需要访问Colleague的Shifts属性,可再加一行ThenInclude // .ThenInclude(c => c.Shifts) .OrderBy(i => i.BranchNumber) .ToListAsync();
问题解决后更新
所有遇到同类问题的开发者可参考上述解决方案。如果想要深入了解ThenInclude方法,可以自行查询EF Core官方相关文档;微软官方文档中也有对应教程,可自行查找针对Razor Pages的相关教程,也可找到对应MVC版本的教程。
内容的提问来源于stack exchange,提问作者Colin-G-Davidson

