将集合数据从HTML / Razor表单发送/ POST到MVC操作参数模型

我正在开展一个项目,帮助学生和顾问使用ASP.NET MVC 5为下一学期选择最好的课程。第一步是让学生从列表中选择他已经学过的课程。 显示列表的控制器是:

public ActionResult AddCourseVM (int? id) { Student student = db.Students.Find(id); // List potentialCourses = student.StudentConcentration.RequiredCourses.ToList(); List potentialCourses = db.BaseCourses.ToList(); AddCourseViewModel vModel = new AddCourseViewModel(student, potentialCourses); List listCourses = new List(); foreach(BaseCourse baseC in potentialCourses) { Course c = new Course(); c.BaseCourse = baseC; c.Student = student; listCourses.Add(c); } vModel.PossibleCourses = listCourses; return View("AddCourseVM", vModel); } 

ViewModel是:

 public class AddCourseViewModel { public Student Student { get; set; } public List AvailCourses { get; set; } public List PossibleCourses { get; set; } public AddCourseViewModel(Student s, List c) { Student = s; AvailCourses = c; PossibleCourses = new List(); } public AddCourseViewModel() { Student = new Student(); AvailCourses = new List(); PossibleCourses = new List(); } } 

课程对象是课程的特定实例(对于给定学生,在某个学期等),BaseCourse对象是课程目录中的单个课程。

我正在使用此视图在列表中显示可能的课程:

  @model CMPSAdvising.ViewModels.AddCourseViewModel @{ ViewBag.Title = "AddCourseVM"; } 

Add Courses

Name: @Model.Student.FirstName @Model.Student.LastName

W#: @Model.Student.WNumber

Select the Courses You Have Taken

@using (Html.BeginForm("AddCourseVM","Students")) { @Html.AntiForgeryToken();
@foreach (var course in Model.PossibleCourses) { }
Course Department Number Check if Taken Semester Grade
@course.BaseCourse.Name @course.BaseCourse.CourseNumber @course.BaseCourse.CourseNumber @Html.CheckBoxFor(s => course.Selected) @Html.TextBoxFor(m => course.Semester) @Html.TextBoxFor(g => course.Grade)
}

最后是当用户点击按钮时接收POST的控制器:

 [HttpPost] [ValidateAntiForgeryToken] public ActionResult AddCourseVM (AddCourseViewModel vModel) { Student stu = vModel.Student; foreach (Course c in vModel.PossibleCourses) { if (c.Selected) { stu.CoursesTaken.Add(c); } } db.Entry(stu).State = EntityState.Modified; db.SaveChanges(); return RedirectToAction("ListTakenCourses", new { id = stu.ID }); } 

我的问题是AddCourseViewModel对象(vModel)返回null。 我希望将ViewModel从网页上作为对象返回,或者至少获取已检查的课程列表和学生的ID。

我相信有一个应用程序称为BeginCollectionItem HtmlHelper 。 这里引用的内容将简要讨论 ,并基于Steve Sanderson几年前撰写的博客文章 。

问题是与表单集合的模型绑定与MVC中与标量输入的模型绑定不完全相同。 您的集合需要一个索引器, 如此处所述 。 如果它没有按预期绑定,请检查呈现的表单输入的名称属性,并将它们与post model(action argument)类中的属性名称和结构进行比较。

从它的外观来看,看起来更像这样的HTML输出应该导致action参数不为null:

  HTTP 101 HTP-101 HTP-101      MVC 101 MVC-101 MVC-101     

…等等,以下razor应该输出:

 @for (var i = 0; i <= Model.PossibleCourses.Count; i++) { var course = Model.PossibleCourses[i];  @course.BaseCourse.Name @course.BaseCourse.CourseNumber @course.BaseCourse.CourseNumber @Html.CheckBox(string.Format("PossibleCourses[{0}].Selected", i), course.Selected) @Html.TextBox(string.Format("PossibleCourses[{0}].Semester", i), course.Semester) @Html.TextBox(string.Format("PossibleCourses[{0}].Grade", i), course.Grade)  } 

请注意,表单输入元素的名称属性如何对应于操作参数Model中的可索引( List )属性的名称,以及包含在集合内的索引( Course )属性名称。 这是一种帮助模型绑定器弄清楚如何使用数据填充操作参数类实例的方法,方法是使输入名称属性与方法参数属性名称匹配。

您还可以使用GUID(或任何字符串)作为索引器,这是BeginItemCollection在内部执行的操作。 以下内容还应该有助于模型绑定器能够填充action参数,以便它不会作为null进入action方法:

 @foreach (var course in Model.PossibleCourses) { var indexer = Guid.NewGuid(); // or possibly course.CourseId  @course.BaseCourse.Name @course.BaseCourse.CourseNumber @course.BaseCourse.CourseNumber @Html.Hidden("PossibleCourses.index", indexer) @Html.CheckBox(string.Format("PossibleCourses[{0}].Selected", indexer), course.Selected) @Html.TextBox(string.Format("PossibleCourses[{0}].Semester", indexer), course.Semester) @Html.TextBox(string.Format("PossibleCourses[{0}].Grade", indexer), course.Grade)  } 

重要的是每个对应于action参数类中的集合项的表单元素组必须共享相同的索引器,并且索引器必须与对应于action参数类中的不同集合项的其他表单元素组不同。 通过阅读该问题及其答案,可以理解该解决方案的示例。