使用 ASP.NET Core MVC MultiSelectList 更新多对多实体

Update many-to-many entity with ASP.NET Core MVC MultiSelectList

我有一个表单,我想在其中更新与另一个实体具有多对多关系的实体,并且我正在尝试使用 select 标记来创建 MultiSelectList控制。我正在使用 ASP.NET Core 1.0 和 Entity Framework.

假设我有以下实体:

public class Student
{
    public int Id { get; set; }
    public string Name { get; set; }
    public List<StudentCourse> Courses { get; set; }
}

public class Course
{
    public int Id { get; set; }
    public string Name { get; set; }
    public List<StudentCourse> Students { get; set; }
}

public class StudentCourse
{
    public int StudentId { get; set; }
    public Student Student { get; set; }

    public int CourseId { get; set; }
    public Course Course { get; set; }
}

视图模型:

public class StudentVM
{
    public Student Student { get; set; }
    public IEnumerable<SelectListItem> CourseList { get; set; }
}

观点:

@model StudentVM
<form asp-action="Edit">
    <div class="form-horizontal>
        <div class="form-group">
            <select asp-for="CourseList" asp-items="Model.CourseList" />
        </div>
        <div class="form-group">
            <div class="col-md-offset-2 col-md-10">
                <input type="submit" value="Save" class="btn btn-default" />
            </div>
        </div>
    </div>
</form>

控制器的动作方法:

[HttpGet]
public async Task<IActionResult> Edit(Student student)
{
    var viewmodel = new StudentVM {
        Student = student,
        CourseList = new MultiSelectList(
            _db.Courses,
            "Id",
            "Name",
            student.Courses
            .Select(sc => sc.CourseId));
    };
    return View(viewmodel);
}

[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(StudentVM viewmodel)
{
    if (viewmodel.Student == null)
        return NotFound();

    if (ModelState.IsValid)
    {
        Student student = viewmodel.Student;
        var newCourses = viewmodel.CourseList
                         .Where(i => i.Selected)
                         .Select(c => new StudentCourse {
                             StudentId = student.Id,
                             CourseId = Convert.ToInt32(c.Value)
                         });
        student.Courses.RemoveAll(sc => !newCourses.Contains(sc));
        student.Courses.AddRange(
            newCourses.Where(nc => !student.Courses.Contains(nc)));
        _db.Update(student);
        _db.SaveChanges();

        return RedirectToAction("Index");
    }
    return View(viewmodel);
}

现在问题来了...

问题 1

StudentVM进入EditHttpPost变体时,两个属性都是null。为什么在进入 Edit 函数之前清除视图模型中的属性?我已确认它们在 HttpGet 变体中设置正确,并且数据在视图中正确显示。

问题 2

由于进入 Edit 的数据无效,我无法测试我的数据库更新。我从 SelectList 中获取所选项目和从 Student.Courses 中获取 adding/removing 项目的方法是否正确?我找不到很多关于 EF Core 中多对多关系的文档。

我是 ASP.NET 和 Entity Framework 的新手,所以任何提示或批评都将不胜感激。

您不能将 <select> 绑定到复杂对象的集合(这就是 MultiSelectList 的含义。<select multiple> 元素回传其选定选项值的数组(在你的案例将是课程 Id 值的数组。你的模型需要一个 属性 来绑定。假设他 Id 属性 of Courseint,然后添加下面的属性

public IEnumerable<int> SelectedCourses { get; set; }

另请注意,在 MultiSelectList 构造函数中设置 selectedValues 属性 会被标签助手忽略(在内部,该方法会根据值构建一个新的 IEnumerable<SelectListItem> 属性。GET 方法中的代码应该是

var viewmodel = new StudentVM
{
    Student = student,
    CourseList = new SelectList(_db.Courses, "Id", "Name"),
    SelectedCourses = student.Courses.Select(sc => sc.CourseId)   
};
return View(viewmodel);

然后在视图中,使用

<select asp-for="SelectedCourses" asp-items="Model.CourseList"></select>

并且在 POST 方法中,SelectedCourses 的值将包含您在视图中选择的课程 Id 值的数组。