EF Core一对多关系异常:仅能获取最后插入的实体
问题描述
我有一个采用MVC模式和Entity Framework Core的Web项目,通过EF DBFirst方式生成了Company(公司)和Plant(工厂)两个模型,二者为一对多关系(一个公司对应多个工厂)。希望在视图中向Plant表提交新工厂数据,并展示该公司的所有工厂数据。目前数据能成功存入数据库,但控制器存在异常:每次提交新工厂后,Company的Plants字段仅保留最后插入的工厂,而非全部。
错误原因分析
- 控制器生命周期限制:ASP.NET Core控制器是瞬态对象,每次HTTP请求都会创建新实例。控制器中声明的
_companyDeclared、_holdPlants字段,以及构造函数注入的List<Plant>和Company,每次请求都会被重新初始化,无法跨请求保存状态。 - EF导航属性未加载:
_context.Companys.Find(companyId)仅查询Company的基础数据,默认不会自动加载关联的Plants集合,导致_companyDeclared.Plants初始为空列表,添加新Plant后仅包含当前一条数据。 - 状态保存逻辑错误:试图用
_holdPlants跨请求保存工厂列表,但每次请求都会重置该列表,导致视图仅能显示最新添加的工厂。
正确实现方案
调整控制器代码
移除控制器级别的状态字段,每次请求直接从数据库加载目标公司的完整数据:
using System.Diagnostics; using Data.Models; using Microsoft.AspNetCore.Mvc; using UI.Models; using Microsoft.EntityFrameworkCore; using Newtonsoft.Json; namespace UI.Controllers; public class HomeController : Controller { private readonly ILogger<HomeController> _logger; private readonly ProjectContext _context; // 仅保留必要的依赖注入服务 public HomeController(ILogger<HomeController> logger, ProjectContext context) { _logger = logger; _context = context; } public IActionResult Index() { return View(); } // 完善Company信息 [HttpPost] public ActionResult CompanyDetail(Company company) { if (ModelState.IsValid) { short companyId = JsonConvert.DeserializeObject<short>(TempData["CompanyId"].ToString()); TempData.Keep("CompanyId"); var companyToUpdate = _context.Companys.Find(companyId); if (companyToUpdate != null) { companyToUpdate.CompanyName = company.CompanyName; _context.SaveChanges(); } return RedirectToAction("PlantsDetail"); } return View(); } // GET请求加载工厂列表 public IActionResult PlantsDetail() { short companyId = JsonConvert.DeserializeObject<short>(TempData["CompanyId"].ToString()); TempData.Keep("CompanyId"); // 显式加载公司及其关联的所有工厂 var companyWithPlants = _context.Companys .Include(c => c.Plants) .FirstOrDefault(c => c.CompanyId == companyId); ViewBag.myCompany = companyWithPlants; ViewBag.plantsList = companyWithPlants?.Plants ?? new List<Plant>(); return View(); } // 提交新工厂 [HttpPost] public ActionResult PlantsDetail(Plant plant) { if (ModelState.IsValid) { short companyId = JsonConvert.DeserializeObject<short>(TempData["CompanyId"].ToString()); TempData.Keep("CompanyId"); // 创建新工厂并关联公司ID var newPlant = new Plant { CompanyId = companyId, PlantNo = plant.PlantNo, PlantName = plant.PlantName }; _context.Plants.Add(newPlant); _context.SaveChanges(); // 保存后重定向到GET方法,加载最新工厂列表 return RedirectToAction(nameof(PlantsDetail)); } // 验证失败时,重新加载当前公司的工厂列表 short companyId = JsonConvert.DeserializeObject<short>(TempData["CompanyId"].ToString()); TempData.Keep("CompanyId"); var companyWithPlants = _context.Companys .Include(c => c.Plants) .FirstOrDefault(c => c.CompanyId == companyId); ViewBag.myCompany = companyWithPlants; ViewBag.plantsList = companyWithPlants?.Plants ?? new List<Plant>(); return View(plant); } }
调整视图代码
修复HTML结构错误,规范表格渲染:
@if (ViewBag.myCompany != null) { <table class="table"> <thead> <tr> <th>工厂编号</th> <th>工厂名称</th> <th>操作</th> </tr> </thead> <tbody> @foreach (var item in ViewBag.plantsList) { <tr> <td>@item.PlantNo</td> <td>@item.PlantName</td> <td> <button type="submit" class="btn btn-success">更新</button> <button type="submit" class="btn btn-danger">删除</button> </td> </tr> } </tbody> </table> }
关键改进点
- 移除跨请求状态字段:不再依赖控制器实例保存状态,每次请求直接从数据库获取最新数据,保证数据一致性。
- 显式加载导航属性:使用
Include(c => c.Plants)加载Company关联的所有Plants集合,确保获取完整数据。 - POST后重定向GET:提交新工厂后重定向到
PlantsDetail的GET方法,避免重复提交问题,同时保证视图加载最新的工厂列表。 - 规范视图结构:修复HTML表格的标签嵌套错误,确保页面渲染正确。
内容的提问来源于stack exchange,提问作者vesadeceeger
相关产品推荐
相关产品推荐

