EF Database First场景下View返回Person的Roles列表为空如何修复
问题成因
- 模型绑定过滤了Roles属性:你在Post的Edit方法中用
[Bind(Include = "id,nachname,vorname,winLogon")]限定了只绑定这4个字段,导航属性Roles直接被模型绑定器忽略,所以Post拿到的person对象的Roles属性默认是空集合。 - 导航属性未预加载:Get请求的Edit方法中用
FindAsync查Person时,没有用Include(p => p.Roles)预加载关联的角色数据,如果你关闭了EF的懒加载,那Get返回的Person的Roles本身就为空。 - View代码存在语法错误:
- 循环条件写的是
i < Model.Roles,直接把集合当数值判断,应该取集合的数量i < Model.Roles.Count() - 循环内没有定义
role变量,直接使用会报错 DropDownListFor绑定对象错误,直接绑定整个Roles集合,而不是单个角色的id
- 循环条件写的是
- Post返回View时未重填ViewBag:Post请求的Edit方法中,操作完返回View时没有重新给
ViewBag.allRoles赋值,View拿到的allRoles为空,下拉框无法正常渲染。 - 代码存在多处笔误:
- 角色类的类名是
Rolle,但代码里多处写成Role,拼写不一致 - Save逻辑里的
userRoles变量未定义,属于无效代码
- 角色类的类名是
修复方案
1. 调整Controller逻辑
首先修改Get的Edit方法,预加载Roles:
public async Task<ActionResult> Edit(int? id) { if (id == null) { return RedirectToAction("BadRequest", "Error", new { area = "" }); } // 预加载关联的Roles Person person = await db.Person.Include(p => p.Roles).FirstOrDefaultAsync(p => p.id == id); if (person == null) { return RedirectToAction("NotFound", "Error", new { area = "" }); } ViewBag.allRoles = db.Roles.ToList(); return View(person); }
然后修改Post的Edit方法,优先从数据库取最新的Person数据操作,避免模型绑定的导航属性为空:
[HttpPost] [ValidateAntiForgeryToken] public async Task<ActionResult> Edit([Bind(Include = "id,nachname,vorname,winLogon")] Person person, string command, int? roleId) { // 先从数据库查带Roles的真实实例 var dbPerson = await db.Person.Include(p => p.Roles).FirstOrDefaultAsync(p => p.id == person.id); if (dbPerson == null) { return RedirectToAction("NotFound", "Error", new { area = "" }); } // 同步基础字段 dbPerson.nachname = person.nachname; dbPerson.vorname = person.vorname; dbPerson.winLogon = person.winLogon; Rolle none = new Rolle { id = -1, name = "None" }; var allRoles = db.Roles.ToList(); // 重填ViewBag ViewBag.allRoles = allRoles; if (ModelState.IsValid) { if (command == "remove" && roleId.HasValue) { var toRemove = dbPerson.Roles.FirstOrDefault(x => x.id == roleId); if(toRemove != null) dbPerson.Roles.Remove(toRemove); return View(dbPerson); } if (command == "add") { dbPerson.Roles.Add(none); return View(dbPerson); } if (command == "save") { // 移除临时的none角色 var tempNone = dbPerson.Roles.FirstOrDefault(r => r.id == -1); if(tempNone != null) dbPerson.Roles.Remove(tempNone); // 标记实体为修改状态 db.Entry(dbPerson).State = EntityState.Modified; await db.SaveChangesAsync(); return RedirectToAction("Index"); } } return View(dbPerson); }
2. 修正View代码
@model Models.Person @{ ViewBag.Title = "Edit"; List<Rolle> allRoles = ViewBag.allRoles; } <h2>Edit</h2> @using (Html.BeginForm("Edit", "People", FormMethod.Post, new { enctype = "multipart/form-data" })) { @Html.AntiForgeryToken() <div class="form-horizontal"> <h4>Person</h4> <hr /> @Html.ValidationSummary(true, "", new { @class = "text-danger" }) @Html.HiddenFor(model => model.id) <div class="form-group"> @Html.LabelFor(model => model.nachname, htmlAttributes: new { @class = "control-label col-md-2" }) <div class="col-md-10"> @Html.EditorFor(model => model.nachname, new { htmlAttributes = new { @class = "form-control" } }) @Html.ValidationMessageFor(model => model.nachname, "", new { @class = "text-danger" }) </div> </div> <div class="form-group"> @Html.LabelFor(model => model.vorname, htmlAttributes: new { @class = "control-label col-md-2" }) <div class="col-md-10"> @Html.EditorFor(model => model.vorname, new { htmlAttributes = new { @class = "form-control" } }) @Html.ValidationMessageFor(model => model.vorname, "", new { @class = "text-danger" }) </div> </div> <div class="form-group"> @Html.LabelFor(model => model.winLogon, htmlAttributes: new { @class = "control-label col-md-2" }) <div class="col-md-10"> @Html.EditorFor(model => model.winLogon, new { htmlAttributes = new { @class = "form-control" } }) @Html.ValidationMessageFor(model => model.winLogon, "", new { @class = "text-danger" }) </div> </div> <div class="form-group col-md"> @for (int i = 0; i < Model.Roles.Count(); i++) { var role = Model.Roles.ElementAt(i); @Html.LabelFor(model => model.Roles, new { @class = "control-label col-md-2" }) <div class="col-md-10"> @Html.DropDownListFor(model => model.Roles.ElementAt(i).id, new SelectList(allRoles, "id", "name", role.id), new { @class = "form-control-static col-md-1" }) <input type="hidden" name="roleId" value="@role.id" /> <input name="command" type="submit" value="remove" class="btn btn-default col-md-offset-1" /> </div> } <input name="command" type="submit" value="add" class="btn btn-default col-md-offset-1" /> </div> <div class="form-group"> <div class="col-md-offset-2 col-md-10"> <input name="command" type="submit" value="save" class="btn btn-default" /> </div> </div> </div> } <div> @Html.ActionLink("Back to List", "Index") </div>
3. 统一命名
要么把角色类名从Rolle改成Role,要么把代码里所有用到Role的地方都改成Rolle,避免拼写错误导致的编译异常。
内容的提问来源于stack exchange,提问作者Pablu
相关产品推荐
相关产品推荐

