如何在传递到ActionResult时绑定可变数量的列表项
Regs列表的绑定与遍历处理问题 看起来你已经搭好了正确的ViewModel和视图基础,只需要调整控制器的参数绑定逻辑,就能轻松处理任意数量的Regs项了。下面是具体的修改步骤:
1. 调整控制器Action参数,绑定整个列表
你当前的代码只能绑定单个Regs[0],只要把参数改成List<SESRegulationsList>类型,并指定正确的前缀,ASP.NET MVC的模型绑定器会自动识别Regs[n]格式的表单字段,把它们组装成完整的列表。修改后的控制器代码如下:
[HttpPost] [ValidateAntiForgeryToken] public ActionResult Index( [Bind(Prefix = "Main", Include = "AlterationForseenChanges,AlterationGrowthDecline,DescriptionEcozone,DescriptionEcoregion,DescriptionEcodistrict,DescriptionPreviousDB")] SESData sESData, [Bind(Prefix = "Regs", Include = "RegulationName,RegulationDescription,RegulationURL")] List<SESRegulationsList> regsList) { // 后续逻辑见下方修改 }
这里的核心是:视图生成的表单字段name属性是Regs[0].RegulationName、Regs[1].RegulationDescription这种索引式命名,MVC会自动把这类字段映射到List<SESRegulationsList>参数中。
2. 遍历列表,批量保存所有Regs项
原来的代码只保存单个SESRegulationList实例,现在需要遍历传入的regsList,把每一项都添加到数据库。另外要注意,如果SESRegulationsList和SESData是关联实体(比如Regs属于某个SESData),必须设置外键关系,否则数据库会因为外键约束报错:
if (ModelState.IsValid) { db.SESData.Add(sESData); // 遍历所有Regs项,设置关联后添加到数据库 foreach (var reg in regsList) { // 假设SESRegulationsList有SESDataId外键字段,绑定到刚添加的SESData reg.SESDataId = sESData.Id; db.SESRegulationList.Add(reg); } db.SaveChanges(); return RedirectToAction("Report", new { id = sESData.id }); } // 验证失败时,返回ViewModel保留用户输入内容 return View(new SESDataViewModel { Main = sESData, Regs = regsList });
小提示:如果你的
SESData是自增主键,先执行db.SESData.Add(sESData)后,EF会为sESData临时分配一个Id,此时设置reg.SESDataId就能正确关联,最后一次SaveChanges会同时保存SESData和所有Regs项。
3. 优化视图渲染方式(可选)
你现在手动写Regs[0]、Regs[1]...的方式不够灵活,要是需要支持用户动态添加Regs项,可以用MVC的EditorTemplate自动渲染整个列表:
- 在
Views/Shared/EditorTemplates文件夹下创建SESRegulationsList.cshtml文件(文件名必须和实体类名一致),内容为单个Regs项的表单控件:
@model SESRegulationsList <div class="reg-item"> <div class="form-group"> @Html.LabelFor(m => m.RegulationName) @Html.EditorFor(m => m.RegulationName, new { htmlAttributes = new { @class = "form-control" } }) @Html.ValidationMessageFor(m => m.RegulationName, "", new { @class = "text-danger" }) </div> <div class="form-group"> @Html.LabelFor(m => m.RegulationDescription) @Html.EditorFor(m => m.RegulationDescription, new { htmlAttributes = new { @class = "form-control" } }) @Html.ValidationMessageFor(m => m.RegulationDescription, "", new { @class = "text-danger" }) </div> <div class="form-group"> @Html.LabelFor(m => m.RegulationURL) @Html.EditorFor(m => m.RegulationURL, new { htmlAttributes = new { @class = "form-control" } }) @Html.ValidationMessageFor(m => m.RegulationURL, "", new { @class = "text-danger" }) </div> </div>
- 在主视图中,用一行代码渲染整个Regs列表:
@Html.EditorFor(m => m.Regs)
这样不管ViewModel里有多少个Regs项,都会自动渲染成正确的表单字段,后续用JS动态添加新项时,只要保持Regs[index].PropertyName的命名格式,模型绑定器就能正确识别。
4. 初始化ViewModel(重要)
在GET方法的Index中,必须初始化ViewModel的Regs列表,避免视图渲染时出现空引用错误:
public ActionResult Index() { var viewModel = new SESDataViewModel { Main = new SESData(), Regs = new List<SESRegulationsList> { new SESRegulationsList() } // 默认加载一个空项 }; return View(viewModel); }
这样用户打开页面时就能看到第一个Regs输入框,后续可以通过JS动态添加更多项。
内容的提问来源于stack exchange,提问作者GPower

