Razor Pages项目中EF无法访问Case外键属性问题排查
问题:Entity Framework关联Case属性无法在Razor Pages视图中显示
问题场景
在Razor Pages项目中,通过Entity Framework关联实体显示外键属性值时,Issue关联的Specialist属性(如FullName、Laboratory)能正常渲染,但Case的属性(如CaseNumber、Principal)无内容展示。
相关代码如下:
视图代码
@foreach (var item in Model.UserIssues) { <div class="card scroll" style="width:22rem;"> <div class="card-header p-4"> <div class="d-flex justify-content-between"> <div> <p class="card-label mb-0"></p> <h5>@Html.DisplayFor(i => item.Case.CaseNumber)</h5> </div> <div> <a class="btn button-idle-add" data-bs-toggle="tooltip" data-bs-title="Dodaj Pracownię" asp-route-id="@item.Id" asp-page="/Cases/AddLaboratory"><i class="fa-solid fa-plus"></i></a> </div> </div> <div class="d-flex justify-content-between mt-3"> <div> <p class="card-input">@Html.DisplayFor(e => item.Case.Principal)</p> </div> <div> <p class="card-input">@Html.DisplayFor(e => item.Case.Date)</p> </div> </div> </div> <div class="card-body"> <div class="m-1 p-3 card-lab"> <div class="d-flex justify-content-between align-items-center"> <p>@Html.DisplayFor(e => item.IssueNumber)</p> <p class="pe-3 ps-3" style="border-radius:15px; background-color: palegreen;"></p> </div> <p>@Html.DisplayFor(e => item.Specialist.Laboratory)</p> <p>@Html.DisplayFor(e => item.Specialist.FullName)</p> </div> </div> </div> }
PageModel代码
public class IndexModel : PageModel { private readonly IRepository<Issue> issueRepository; private readonly IRepository<Specialist> specialistRepository; public IndexModel(IRepository<Issue> issueRepository, IRepository<Specialist> specialistRepository) { this.issueRepository = issueRepository; this.specialistRepository = specialistRepository; } public List<Issue> UserIssues { get; set; } public async Task<IActionResult> OnGetAsync() { var loggedUser = specialistRepository.GetAll().FirstOrDefault(u => u.Login == User.Identity.Name); UserIssues = issueRepository.GetAll().Where(i => i.Specialist.Id == loggedUser.Id).ToList(); return Page(); } }
实体模型代码
Case类
public class Case { [Key] public int Id { get; set; } [Required(ErrorMessage = "Uzupełnij pole")] public string CaseNumber { get; set; } [Required(ErrorMessage = "Uzupełnij pole")] public string Principal { get; set; } [Required(ErrorMessage = "Uzupełnij pole")] public string Description { get; set; } [Required(ErrorMessage = "Uzupełnij pole")] public string Date { get; set; } public ICollection<Issue> Issues { get; set; } }
Issue类
public class Issue { [Key] public int Id { get; set; } public string Comment { get; set; } [Required(ErrorMessage = "Uzupełnij pole")] public string IssueNumber { get; set; } public Case Case { get; set; } public Specialist Specialist { get; set; } }
Specialist类
public class Specialist : IdentityUser { [Required] public string Login { get; set; } [Required] public string FirstName { get; set; } [Required] public string LastName { get; set; } [Required] public string Laboratory { get; set; } public string FullName { get { return FirstName + " " + LastName; } } public ICollection<Issue> Issues { get; set; } }
问题原因
- 缺少显式外键字段:
Issue实体仅定义了Case导航属性,但未添加对应的CaseId外键字段。Entity Framework默认需要外键明确关联关系,缺少外键可能导致关联数据无法正确映射。 - 未显式加载关联实体:查询
Issue时未使用Include方法加载关联的Case数据。如果仓库的GetAll()方法未处理关联加载,且延迟加载未开启,EF不会自动获取Case的相关数据。
解决方案
步骤1:为Issue实体添加外键字段
修改Issue类,添加CaseId外键(同时建议为Specialist添加外键,保持一致性):
public class Issue { [Key] public int Id { get; set; } public string Comment { get; set; } [Required(ErrorMessage = "Uzupełnij pole")] public string IssueNumber { get; set; } // 添加Case外键 public int CaseId { get; set; } public Case Case { get; set; } // 添加Specialist外键(IdentityUser主键为string) public string SpecialistId { get; set; } public Specialist Specialist { get; set; } }
步骤2:更新数据库
添加外键后,生成新的EF迁移并更新数据库,确保数据库表中新增CaseId和SpecialistId字段:
Add-Migration AddForeignKeysToIssue Update-Database
步骤3:查询时显式加载关联实体
修改PageModel中的查询逻辑,使用Include方法加载Case和Specialist关联数据。如果仓库的GetAll()返回IQueryable<T>,可直接链式调用:
public async Task<IActionResult> OnGetAsync() { var loggedUser = specialistRepository.GetAll().FirstOrDefault(u => u.Login == User.Identity.Name); UserIssues = issueRepository.GetAll() .Include(i => i.Case) // 加载关联的Case .Include(i => i.Specialist) // 显式加载Specialist(确保稳定性) .Where(i => i.Specialist.Id == loggedUser.Id) .ToList(); return Page(); }
如果仓库的GetAll()返回List<T>而非IQueryable<T>,需要修改仓库接口和实现,支持关联加载:
扩展IRepository接口
public interface IRepository<T> { IQueryable<T> GetAll(); // 添加支持关联加载的方法 IQueryable<T> GetAllIncluding(params Expression<Func<T, object>>[] includes); }
实现仓库方法
public class IssueRepository : IRepository<Issue> { private readonly ApplicationDbContext _context; public IssueRepository(ApplicationDbContext context) { _context = context; } public IQueryable<Issue> GetAll() { return _context.Issues.AsQueryable(); } public IQueryable<Issue> GetAllIncluding(params Expression<Func<Issue, object>>[] includes) { var query = _context.Issues.AsQueryable(); foreach (var include in includes) { query = query.Include(include); } return query; } }
修改PageModel调用
UserIssues = issueRepository.GetAllIncluding(i => i.Case, i => i.Specialist) .Where(i => i.Specialist.Id == loggedUser.Id) .ToList();
可选:验证延迟加载配置
如果希望依赖延迟加载(不推荐用于查询场景,会导致N+1问题),需确保EF上下文已开启延迟加载:
public class ApplicationDbContext : IdentityDbContext<Specialist> { public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options) { // 开启延迟加载(需安装Microsoft.EntityFrameworkCore.Proxies包) this.ChangeTracker.LazyLoadingEnabled = true; } public DbSet<Case> Cases { get; set; } public DbSet<Issue> Issues { get; set; } }
验证
完成上述修改后,重新运行项目,视图中Case的相关属性(CaseNumber、Principal等)即可正常显示。
内容的提问来源于stack exchange,提问作者Just Nicolas
相关产品推荐
相关产品推荐

