.NET 6中视图循环内选中项值无法完整传递至控制器求助
解决方案
问题根源
当前视图循环生成了多个同名的表单控件(如name="EmployeeID"),但ViewModel中对应的是单个值属性,ASP.NET Core模型绑定只能捕获最后一个(或第一个)控件的值,无法收集所有行的选择结果。需要将ViewModel调整为集合结构,让表单控件生成带索引的name属性,确保模型绑定能识别所有条目。
1. 调整ViewModel结构
创建子模型存储单天的排班信息,再在主模型中添加该子模型的集合:
// 子模型:存储单条排班记录 public class EmpShiftItem { public string Date { get; set; } public int? EmployeeID { get; set; } // 改为可空类型,避免未选择时传入默认值0 public int? ShiftsID { get; set; } public string Priority { get; set; } } // 主视图模型 public class EmpShifts { public int DepartmentID { get; set; } public IEnumerable<Departments>? Departments { get; set; } public IEnumerable<Employee>? Employees { get; set; } = new List<Employee>(); public IEnumerable<Shifts>? Shifts { get; set; } = new List<Shifts>(); // 移除原单个的EmployeeID、ShiftsID,改用集合存储所有排班条目 public List<EmpShiftItem> ShiftItems { get; set; } = new List<EmpShiftItem>(); }
2. 更新控制器初始化逻辑
在GetSheduleWithEmp方法中,为ShiftItems初始化数据,确保每个日期对应一个空条目:
public async Task<IActionResult> GetSheduleWithEmp(int DepartmentId, DateTime Start, DateTime End) { List<string> dates = Enumerable.Range(0, 1 + End.Subtract(Start).Days) .Select(i => Start.AddDays(i).ToString("dd-MM-yyyy")) .ToList(); if (DepartmentId != 0) { var Shifts = await eRDbContext.Shifts.ToListAsync(); var model = await eRDbContext.Employees.Where(x => x.DepartmentId == DepartmentId).ToListAsync(); EmpShifts EmpShiftsView = new EmpShifts { DepartmentID = DepartmentId, Employees= model, Shifts = Shifts, // 初始化ShiftItems,每个日期对应一个空条目 ShiftItems = dates.Select(date => new EmpShiftItem { Date = date }).ToList() }; return View("AddEmployeeToShifts", EmpShiftsView); } return View(); }
3. 修改视图渲染逻辑
使用ShiftItems的索引生成表单控件,确保每个控件的name属性带索引,让模型绑定能识别所有条目:
<form asp-action="SaveShifts" class="form" method="post"> @if (Model.ShiftItems != null && Model.ShiftItems.Any()) { <table class="table table-striped"> <thead> <tr> <th>Date</th> <th>Employee</th> <th>Shift</th> <th>Priority</th> </tr> </thead> <tbody> @for (int i = 0; i < Model.ShiftItems.Count; i++) { <tr> <td> @Model.ShiftItems[i].Date <!-- 隐藏域提交日期,确保后台关联对应排班 --> <input type="hidden" asp-for="ShiftItems[i].Date" /> </td> <td> <select class="form-select" asp-for="ShiftItems[i].EmployeeID" asp-items="@(new SelectList(Model.Employees,"Id","Name"))"> <option value="">Please Select Employee</option> </select> </td> <td> <select class="form-select" asp-for="ShiftItems[i].ShiftsID" asp-items="@(new SelectList(Model.Shifts,"Id","Name"))"> <option value="">Please Select Shift</option> </select> </td> <td> <select class="form-select" asp-for="ShiftItems[i].Priority"> <option value="1st Oncall">1st Oncall</option> <option value="2nd Oncall">2nd Oncall</option> <option value="3rd Oncall">3rd Oncall</option> </select> </td> </tr> } </tbody> </table> <div class="col-12"> <button type="submit" class="btn btn-primary mx-auto">Submit</button> </div> } </form>
4. 接收提交数据的控制器方法
在SaveShifts方法中,直接接收EmpShifts模型,此时ShiftItems集合会包含所有行的选择数据:
using System.Globalization; [HttpPost] public async Task<IActionResult> SaveShifts(EmpShifts model) { if (!ModelState.IsValid) { // 验证失败时重新填充员工、班次列表并返回视图 model.Employees = await eRDbContext.Employees.Where(x => x.DepartmentId == model.DepartmentID).ToListAsync(); model.Shifts = await eRDbContext.Shifts.ToListAsync(); return View("AddEmployeeToShifts", model); } // 遍历所有排班条目,执行保存逻辑 foreach (var item in model.ShiftItems) { if (item.EmployeeID.HasValue && item.ShiftsID.HasValue) { // 替换为你的排班实体类 var schedule = new Schedule { DepartmentId = model.DepartmentID, EmployeeId = item.EmployeeID.Value, ShiftId = item.ShiftsID.Value, Date = DateTime.ParseExact(item.Date, "dd-MM-yyyy", CultureInfo.InvariantCulture), Priority = item.Priority }; eRDbContext.Schedules.Add(schedule); } } await eRDbContext.SaveChangesAsync(); return RedirectToAction("YourSuccessAction"); }
关键说明
asp-for="ShiftItems[i].XXX"会自动生成带索引的name属性(如ShiftItems[0].EmployeeID),ASP.NET Core模型绑定会自动将这些值映射到集合中。- 子模型使用可空
int?类型,避免用户未选择时传入默认值0,便于判断必填项。 - 添加隐藏域传递日期,确保后台能准确关联每条排班记录对应的日期。
内容的提问来源于stack exchange,提问作者Abdulmajeed
相关产品推荐
相关产品推荐

