保存MVC上的多对多关系数据创build视图

我在保存创build视图结果的多对多关系中遇到了一些问题。

我想为新的用户configuration文件创build一个新的页面,该页面有一个允许他们select课程(多对多关系)的清单。 我的视图从Courses数据库中获取logging,并用checkbox显示它们。

一旦用户发布数据,我想更新我的用户configuration文件模型,以及courses之间的多对多关系。 这是我错过的代码!

我在MVC是新手,我一直在研究,但我还没有做到这一点。

我正在以下示例: http : //www.asp.net/mvc/tutorials/getting-started-with-ef-using-mvc/updating-related-data-with-the-entity-framework-in-an- ASP净MVC应用程序

这是模型:

 public class UserProfile { public int Id { get; set; } public string Name { get; set; } public virtual ICollection<Courses> usercourses { get; set; } } public class Courses { public int CourseID { get; set; } public string CourseDescripcion { get; set; } public virtual ICollection<UserProfile> UserProfiles { get; set; } } public class UserProfileDBContext : DbContext { public DbSet<UserProfile> UserProfiles { get; set; } public DbSet<Courses> usrCourses{ get; set; } } 

我还添加了一个ViewModel:

 namespace Mysolution.ViewModels { public class AssignedCourseData { public int CourseID { get; set; } public string CourseDescription { get; set; } public bool Assigned { get; set; } } } 

这是create填充课程checkbox的控制器:

 public ActionResult Create() { PopulateCoursesData(); return View(); } private void PopulateCoursesData() { var CoursesData = db.usrCourses; var viewModel = new List<AssignedCourseData>(); foreach (var item in CoursesData) { viewModel.Add(new AssignedCourseData { CourseID = item.CourseID, CourseDescription = item.CourseDescription, Assigned = false }); } ViewBag.CoursePopulate = viewModel; } 

这是观点

 @{ int cnt = 0; List<MySolution.ViewModels.AssignedCourseData> courses = ViewBag.CoursePopulate; foreach (var course in courses) { <input type="checkbox" name="selectedCourse" value="@course.CourseID" /> @course.CourseDescription } } 

这是获取数据的控制器(以及我想要保存的数据)。 它作为一个参数string[] selectedCourse为checkbox:

 [HttpPost] public ActionResult Create(UserProfile userprofile, string[] selectedCourse) { if (ModelState.IsValid) { db.UserProfiles.Add(userprofile); //TO DO: Save data from many to many (this is what I can't do!) db.SaveChanges(); } return View(userprofile); } 

编辑 :我已经写了3个博客文章的代码

  • 第1部分设置解决scheme并创build一个新用户
  • 第2部分添加课程并将其保存为用户configuration文件
  • 第3部分允许编辑和删除用户及其课程

Github源: https : //github.com/cbruen1/mvc4-many-to-many


例如,我认为你已经在一些命名中偏离了惯例,所以我已经在我认为合适的地方进行了修改。 在我看来,将这些课程作为UserProfile的一部分发布出去的最好方法是让他们通过一个编辑器模板进行渲染,我将在后面进一步解释。

以下是我将如何实现这一切:

(感谢@Slauma在保存新课程时指出错误)。

  • 在你的模型中,你的“课程”类是一个单一的实体,通常被命名为课程,课程集合将被命名为课程。
  • 而不是在ViewBag中使用AssignedCourseData使用视图模型
  • 在创build一个新用户的时候实现这个function – 即为Userprofile创build一个标准的Create视图,该视图包含一个AssignedCourseData视图模型,该视图模型将被返回UserProfileViewModel。

从数据库开始,将UserProfile集合保持不变,并命名课程集合课程:

 public DbSet<UserProfile> UserProfiles { get; set; } public DbSet<Course> Courses { get; set; } 

在DbContext类中重写OnModelCreating方法。 这是你如何映射UserProfile和课程之间的多对多关系:

 protected override void OnModelCreating(DbModelBuilder modelBuilder) { modelBuilder.Entity<UserProfile>() .HasMany(up => up.Courses) .WithMany(course => course.UserProfiles) .Map(mc => { mc.ToTable("T_UserProfile_Course"); mc.MapLeftKey("UserProfileID"); mc.MapRightKey("CourseID"); } ); base.OnModelCreating(modelBuilder); } 

我还会在同一个命名空间中添加一个模拟初始化类,它会给你一些开始的课程,并且意味着你不必每次模型改变时手动添加它们。

 public class MockInitializer : DropCreateDatabaseAlways<MVC4PartialViewsContext> { protected override void Seed(MVC4PartialViewsContext context) { base.Seed(context); var course1 = new Course { CourseID = 1, CourseDescripcion = "Bird Watching" }; var course2 = new Course { CourseID = 2, CourseDescripcion = "Basket weaving for beginners" }; var course3 = new Course { CourseID = 3, CourseDescripcion = "Photography 101" }; context.Courses.Add(course1); context.Courses.Add(course2); context.Courses.Add(course3); } } 

将此行添加到Application_Start()Global.asax以启动它:

 Database.SetInitializer(new MockInitializer()); 

所以这里是模型:

 public class UserProfile { public UserProfile() { Courses = new List<Course>(); } public int UserProfileID { get; set; } public string Name { get; set; } public virtual ICollection<Course> Courses { get; set; } } public class Course { public int CourseID { get; set; } public string CourseDescripcion { get; set; } public virtual ICollection<UserProfile> UserProfiles { get; set; } } 

现在在Controller中创build两个新的操作结果来创build一个新的用户configuration文件:

 public ActionResult CreateUserProfile() { var userProfileViewModel = new UserProfileViewModel { Courses = PopulateCourseData() }; return View(userProfileViewModel); } [HttpPost] public ActionResult CreateUserProfile(UserProfileViewModel userProfileViewModel) { if (ModelState.IsValid) { var userProfile = new UserProfile { Name = userProfileViewModel.Name }; AddOrUpdateCourses(userProfile, userProfileViewModel.Courses); db.UserProfiles.Add(userProfile); db.SaveChanges(); return RedirectToAction("Index"); } return View(userProfileViewModel); } 

这里是你的PopulateCourseData类似于你有它,除了不放在ViewBag – 它现在是UserProfileViewModel属性:

 private ICollection<AssignedCourseData> PopulateCourseData() { var courses = db.Courses; var assignedCourses = new List<AssignedCourseData>(); foreach (var item in courses) { assignedCourses.Add(new AssignedCourseData { CourseID = item.CourseID, CourseDescription = item.CourseDescripcion, Assigned = false }); } return assignedCourses; } 

创build一个编辑器模板 – 在你的Views \ Shared文件夹中创build一个名为EditorTemplates的新文件夹(如果你还没有的话)。 添加一个名为AssignedCourseData的新的部分视图,并粘贴下面的代码。 这是一个魔术,正确渲染和命名所有的checkbox – 你不需要为每个循环,因为编辑器模板将创build集合中传递的所有项目:

 @model AssignedCourseData @using MySolution.ViewModels <fieldset> @Html.HiddenFor(model => model.CourseID) @Html.CheckBoxFor(model => model.Assigned) @Html.DisplayFor(model => model.CourseDescription) </fieldset> 

在您的视图模型文件夹中创build一个用户configuration文件视图模型 – 它有一个AssignedCourseData对象的集合:

 public class UserProfileViewModel { public int UserProfileID { get; set; } public string Name { get; set; } public virtual ICollection<AssignedCourseData> Courses { get; set; } } 

添加一个名为CreateUserprofile.cshtml的新视图来创build用户configuration文件 – 您可以在已经添加的CreateUserProfile控制器方法中右键单击并select“添加视图”:

 @model UserProfileViewModel @using MySolution.ViewModels @using (Html.BeginForm("CreateUserProfile", "Course", FormMethod.Post)) { @Html.ValidationSummary(true) <fieldset> @Html.DisplayFor(model => model.Name) @Html.EditorFor(model => model.Name) // Render the check boxes using the Editor Template @Html.EditorFor(x => x.Courses) </fieldset> <p> <input type="submit" value="Create" /> </p> } 

这将正确呈现字段名称,以便在表单被发回给Controller时它们是用户configuration文件视图模型的一部分。 这些字段将被命名为:

 <fieldset> <input data-val="true" data-val-number="The field CourseID must be a number." data-val-required="The CourseID field is required." id="Courses_0__CourseID" name="Courses[0].CourseID" type="hidden" value="1" /> <input data-val="true" data-val-required="The Assigned field is required." id="Courses_0__Assigned" name="Courses[0].Assigned" type="checkbox" value="true" /><input name="Courses[0].Assigned" type="hidden" value="false" /> Bird Watching </fieldset> 

其他字段将被类似地命名,除了分别被索引为1和2。 最后,这里是如何将表格发回时将课程保存到新的用户configuration文件。 将此方法添加到您的控制器中 – 当表单被重新发布时,这将从CreateUserProfile操作结果调用:

 private void AddOrUpdateCourses(UserProfile userProfile, IEnumerable<AssignedCourseData> assignedCourses) { foreach (var assignedCourse in assignedCourses) { if (assignedCourse.Assigned) { var course = new Course { CourseID = assignedCourse.CourseID }; db.Courses.Attach(course); userProfile.Courses.Add(course); } } } 

一旦课程是用户configuration文件的一部分,EF负责关联。 它将为在OnModelCreating中创build的T_UserProfile_Course表中select的每个课程添加logging。 以下是CreateUserProfile操作结果方法,显示返回的课程:

课程复选框张贴回控制器

我select了2门课程,您可以看到课程已添加到新的用户configuration文件对象中:

添加到新的userprofile对象的课程