ASP.NET MVC中CheckBox Partial View绑定模型属性失效问题
我看了你遇到的问题:点击SendSelected按钮后,Post方法里的VirtualServer集合的IsSelectedForRemove属性没有更新。这是因为你在局部视图里用foreach循环渲染集合时,HtmlHelper生成的表单元素name属性格式不对,导致ASP.NET MVC的ModelBinder无法正确识别这些元素属于集合中的哪一项。
问题根源
当你用foreach遍历IEnumerable<VirtualServer>时,生成的CheckBox和Hidden的name属性是server.IsSelectedForRemove和server.VirtualServerId,而ModelBinder需要的格式是VirtualServers[索引].IsSelectedForRemove(其中VirtualServers是你上下文里的集合属性名,索引是元素在集合中的位置)。没有正确的索引标识,ModelBinder无法把表单值映射到对应的集合元素上。
解决方案:两种可行的修复方式
方式一:改用for循环(需要集合支持索引访问)
首先修改局部视图的模型类型为IList<VirtualServer>,这样可以通过索引访问元素,让HtmlHelper生成正确的name属性:
@using VirtualServerManager.Models @model IList<VirtualServer> @for (int i = 0; i < Model.Count; i++) { <tr> <td><p>@Model[i].VirtualServerId</p></td> <td><p>@Model[i].CreateDateTime</p></td> <td><p>@Model[i].RemoveDateTime</p></td> <td> @Html.HiddenFor(m => m[i].VirtualServerId) @Html.CheckBoxFor(m => m[i].IsSelectedForRemove) </td> </tr> }
然后在主视图中传递模型时,把IEnumerable转成IList:
@Html.Partial("GetVirtualServers", Model.VirtualServers.ToList())
方式二:使用EditorTemplate(更优雅的集合渲染方式)
创建一个专门的EditorTemplate来渲染单个VirtualServer元素,MVC会自动为集合元素生成正确的索引化name属性:
- 在
Views/Shared/EditorTemplates文件夹下创建VirtualServer.cshtml(文件名必须和模型类名一致):
@using VirtualServerManager.Models @model VirtualServer <tr> <td><p>@Model.VirtualServerId</p></td> <td><p>@Model.CreateDateTime</p></td> <td><p>@Model.RemoveDateTime</p></td> <td> @Html.HiddenFor(m => m.VirtualServerId) @Html.CheckBoxFor(m => m.IsSelectedForRemove) </td> </tr>
- 修改你的局部视图,直接用
Html.EditorForModel()渲染整个集合:
@using VirtualServerManager.Models @model IEnumerable<VirtualServer> @Html.EditorForModel()
最后修复控制器的Post方法
现在ModelBinder能正确绑定集合了,你需要在Post方法里处理传入的修改,而不是直接返回数据库原数据:
[HttpPost] public PartialViewResult Index(VirtualServersManagerContext model) { // 将表单提交的选中状态同步到数据库上下文 foreach (var submittedServer in model.VirtualServers) { var dbServer = _dataBaseContext.VirtualServers .FirstOrDefault(s => s.VirtualServerId == submittedServer.VirtualServerId); if (dbServer != null) { dbServer.IsSelectedForRemove = submittedServer.IsSelectedForRemove; } } _dataBaseContext.SaveChanges(); // 持久化修改到数据库 return PartialView("GetVirtualServers", _dataBaseContext.VirtualServers); }
这样修改后,当你勾选CheckBox并点击SendSelected,Post方法就能接收到正确的IsSelectedForRemove属性值了。
内容的提问来源于stack exchange,提问作者Сергей

