在前面的教程中,您将显示相关的数据 ;在本教程中,您会更新相关的数据。对于大多数的关系,这个目标是可以通过更新相应的外键字段来达到的。对于多对多关系,实体框架并不直接,暴露联接表,因此您必须显式添加和删除,并从相应的导航属性的实体。
下面的插图显示页面,您将利用工作。
为课程自定义创建和编辑页面
当创建新的课程实体时,它必须拥有一个关系到现行的部门。为了推动这项工作,搭建的代码包括控制器方法,并且创建和编辑视图中包括用于选择处的下拉列表。下拉列表中设置Course.DepartmentID
的外键属性,,这是所有实体框架所需加载具有适当的Department
实体的Department
导航属性。您可以使用搭建的代码,但改变它微微地添加错误处理和下拉列表进行排序。
在CourseController.cs,删除四个Edit
和Create
方法并替换为以下代码:
public ActionResult Create() { PopulateDepartmentsDropDownList(); return View(); } [HttpPost] [ValidateAntiForgeryToken] public ActionResult Create( [Bind(Include = "CourseID,Title,Credits,DepartmentID")] Course course) { try { if (ModelState.IsValid) { db.Courses.Add(course); db.SaveChanges(); return RedirectToAction("Index"); } } catch (DataException /* dex */) { //Log the error (uncomment dex variable name after DataException and add a line here to write a log.) ModelState.AddModelError("", "Unable to save changes. Try again, and if the problem persists, see your system administrator."); } PopulateDepartmentsDropDownList(course.DepartmentID); return View(course); } public ActionResult Edit(int id) { Course course = db.Courses.Find(id); PopulateDepartmentsDropDownList(course.DepartmentID); return View(course); } [HttpPost] [ValidateAntiForgeryToken] public ActionResult Edit( [Bind(Include = "CourseID,Title,Credits,DepartmentID")] Course course) { try { if (ModelState.IsValid) { db.Entry(course).State = EntityState.Modified; db.SaveChanges(); return RedirectToAction("Index"); } } catch (DataException /* dex */) { //Log the error (uncomment dex variable name after DataException and add a line here to write a log.) ModelState.AddModelError("", "Unable to save changes. Try again, and if the problem persists, see your system administrator."); } PopulateDepartmentsDropDownList(course.DepartmentID); return View(course); } private void PopulateDepartmentsDropDownList(object selectedDepartment = null) { var departmentsQuery = from d in db.Departments orderby d.Name select d; ViewBag.DepartmentID = new SelectList(departmentsQuery, "DepartmentID", "Name", selectedDepartment); }
PopulateDepartmentsDropDownList
方法获取列表按名称排序的所有部门、 创建下拉列表,SelectList
的集合并将集合传递到ViewBag
属性中查看。该方法接受可选的selectedDepartment
参数允许调用代码来指定当呈现下拉列表中将选定的项。视图会将DepartmentID
的名称传递给DropDownList
helper,和助手就会知道应该命名为DepartmentID
的SelectList
ViewBag
对象中寻找.
HttpGet
Create
方法调用PopulateDepartmentsDropDownList
方法无需设置所选的项目,因为有一个新课程部尚未确立:
public ActionResult Create() { PopulateDepartmentsDropDownList(); return View(); }
HttpGet
Edit
方法设置所选的项目,基于已分配给课程正在编辑该署的 ID:
public ActionResult Edit(int id) { Course course = db.Courses.Find(id); PopulateDepartmentsDropDownList(course.DepartmentID); return View(course); }
Create
和Edit
的HttpPost
方法还包括设置选定的项,当他们出现错误之后重新显示该页的代码:
catch (DataException /* dex */) { //Log the error (uncomment dex variable name after DataException and add a line here to write a log.) ModelState.AddModelError("", "Unable to save changes. Try again, and if the problem persists, see your system administrator."); } PopulateDepartmentsDropDownList(course.DepartmentID); return View(course);
此代码可确保当要显示错误消息,重新显示页上,不管部入选始终保持选定状态。
在Views\Course\Create.cshtml,添加突出显示的代码,以创建一个新的课程编号字段在标题字段之前。正如解释在早些时候的教程中,主键字段不搭建在默认情况下,但此主键是有意义的所以您希望用户能够输入的密钥值。
@model ContosoUniversity.Models.Course
@{
ViewBag.Title = "Create";
}
<h2>Create</h2> @using (Html.BeginForm()) { @Html.AntiForgeryToken() @Html.ValidationSummary(true) <fieldset> <legend>Course</legend> <div class="editor-label"> @Html.LabelFor(model => model.CourseID) </div> <div class="editor-field"> @Html.EditorFor(model => model.CourseID) @Html.ValidationMessageFor(model => model.CourseID) </div>
<div class="editor-label"> @Html.LabelFor(model => model.Title) </div> <div class="editor-field"> @Html.EditorFor(model => model.Title) @Html.ValidationMessageFor(model => model.Title) </div> <div class="editor-label"> @Html.LabelFor(model => model.Credits) </div> <div class="editor-field"> @Html.EditorFor(model => model.Credits) @Html.ValidationMessageFor(model => model.Credits) </div> <div class="editor-label"> @Html.LabelFor(model => model.DepartmentID, "Department") </div> <div class="editor-field"> @Html.DropDownList("DepartmentID", String.Empty) @Html.ValidationMessageFor(model => model.DepartmentID) </div> <p> <input type="submit" value="Create" /> </p> </fieldset> } <div> @Html.ActionLink("Back to List", "Index") </div> @section Scripts { @Scripts.Render("~/bundles/jqueryval") }
在Views\Course\Edit.cshtml、 Views\Course\Delete.cshtml、 Views\Course\Details.cshtml添加课程数字字段在Title字段之前。因为它是主键,它会显示,但不能更改。
<div class="editor-label"> @Html.LabelFor(model => model.CourseID) </div> <div class="editor-field"> @Html.DisplayFor(model => model.CourseID) </div>
运行创建页 (显示路线索引页面,并单击新建) 和输入一门新课程的数据:
单击创建。课程索引页面显示添加到列表中的新课程。在索引页面列表中的部门名称来自于导航属性,显示正确建立了关系。
运行编辑页 (显示路线索引页面,并在课程上单击编辑)。
更改页面上的数据并单击保存。课程索引页显示已更新的课程数据。
添加Instructors编辑页
当您编辑一个教练记录时,你想要能够更新教师的办公室分配。Instructor
实体具有一个到零或一个关联OfficeAssignment
实体,这就意味着你必须处理以下情况:
- 如果用户清除办公室分配,它原本的价值,您必须删除并删除
OfficeAssignment
实体。 - 如果用户输入的办公室分配值,它最初为空,您必须创建一个新的
OfficeAssignment
实体。 - 如果用户更改办公室被分配的值,则必须更改现有的
OfficeAssignment
实体中的值。
打开InstructorController.cs ,然后看看HttpGet
Edit
方法:
public ActionResult Edit(int id = 0) { Instructor instructor = db.Instructors.Find(id); if (instructor == null) { return HttpNotFound(); } ViewBag.InstructorID = new SelectList(db.OfficeAssignments, "InstructorID", "Location", instructor.InstructorID); return View(instructor); }
这里搭建的代码不是你想要什么。它设置数据为下拉列表,但你你所需要的是一个文本框。此方法替换为以下代码:
public ActionResult Edit(int id) { Instructor instructor = db.Instructors .Include(i => i.OfficeAssignment) .Where(i => i.InstructorID == id) .Single(); return View(instructor); }
这段代码滴ViewBag
声明,并添加预先加载关联的OfficeAssignment
实体。所以相反的Where
和Single
方法用于选择教练的情况下,不能使用Find
方法,执行预先加载。
用以下代码替换HttpPost
Edit
方法。处理办公室工作分配更新:
[HttpPost] [ValidateAntiForgeryToken] public ActionResult Edit(int id, FormCollection formCollection) { var instructorToUpdate = db.Instructors .Include(i => i.OfficeAssignment) .Where(i => i.InstructorID == id) .Single(); if (TryUpdateModel(instructorToUpdate, "", new string[] { "LastName", "FirstMidName", "HireDate", "OfficeAssignment" })) { try { if (String.IsNullOrWhiteSpace(instructorToUpdate.OfficeAssignment.Location)) { instructorToUpdate.OfficeAssignment = null; } db.Entry(instructorToUpdate).State = EntityState.Modified; db.SaveChanges(); return RedirectToAction("Index"); } catch (DataException /* dex */) { //Log the error (uncomment dex variable name after DataException and add a line here to write a log. ModelState.AddModelError("", "Unable to save changes. Try again, and if the problem persists, see your system administrator."); } } ViewBag.InstructorID = new SelectList(db.OfficeAssignments, "InstructorID", "Location", id); return View(instructorToUpdate); }
该代码执行以下任务:
-
获取从数据库使用的当前的
Instructor
实体预先加载OfficeAssignment
导航属性。这是你的所作所为中HttpGet
Edit
方法相同。 -
从模型联编程序的值更新检索到的
Instructor
实体。您想要包括的属性使用的TryUpdateModel重载使您到白名单中。这可以防止过度张贴,如所述第二个教程.if (TryUpdateModel(instructorToUpdate, "", new string[] { "LastName", "FirstMidName", "HireDate", "OfficeAssignment" }))
-
如果办公室位置为空,将设置
Instructor.OfficeAssignment
属性,以便将删除OfficeAssignment
表中的相关的行,则为 null。if (String.IsNullOrWhiteSpace(instructorToUpdate.OfficeAssignment.Location)) { instructorToUpdate.OfficeAssignment = null; }
- 将更改保存到数据库中。
在Views\Instructor\Edit.cshtml后div
元素为雇佣日期字段中,, 添加新的字段编辑办公地点:
<div class="editor-label"> @Html.LabelFor(model => model.OfficeAssignment.Location) </div> <div class="editor-field"> @Html.EditorFor(model => model.OfficeAssignment.Location) @Html.ValidationMessageFor(model => model.OfficeAssignment.Location) </div>
运行该页面 (选择教官选项卡,然后点击编辑讲师)。更改办公地点并单击保存.
在指导老师编辑页添加课程作业
教练可以教任何数目的课程。现在您将通过添加更改课程作业使用一组复选框,如下面的屏幕快照中所示的能力增强导师编辑页面:
Course
和Instructor
实体之间的关系是多,这意味着您没有直接访问到联接表。相反,您将添加和删除实体,从Instructor.Courses
导航属性。
用户界面中,使您能够更改哪些课程的讲师是分配给是一组复选框。显示数据库中的每一门课程的复选框,并选择了那些讲师当前分配给。用户可以选择或清除复选框以更改课程作业。如果课程数目大得多,你可能想要使用不同的方法,在视图中,显示数据,但您将使用相同的方法操纵导航属性以创建或删除关系。
为列表中的复选框提供数据到视图,您将使用一个视图模型类。在Viewmodel文件夹中创建AssignedCourseData.cs和现有的代码替换为以下代码:
namespace ContosoUniversity.ViewModels { public class AssignedCourseData { public int CourseID { get; set; } public string Title { get; set; } public bool Assigned { get; set; } } }
在InstructorController.cs,用下面的代码替换HttpGet
Edit
方法。高亮行为所做的更改。
public ActionResult Edit(int id) { Instructor instructor = db.Instructors .Include(i => i.OfficeAssignment) .Include(i => i.Courses) .Where(i => i.InstructorID == id) .Single(); PopulateAssignedCourseData(instructor); return View(instructor); } private void PopulateAssignedCourseData(Instructor instructor) { var allCourses = db.Courses; var instructorCourses = new HashSet<int>(instructor.Courses.Select(c => c.CourseID)); var viewModel = new List<AssignedCourseData>(); foreach (var course in allCourses) { viewModel.Add(new AssignedCourseData { CourseID = course.CourseID, Title = course.Title, Assigned = instructorCourses.Contains(course.CourseID) }); } ViewBag.Courses = viewModel; }
该代码添加预先加载Courses
导航属性,调用新的PopulateAssignedCourseData
方法,以便为使用AssignedCourseData
视图模型类的复选框数组提供的信息。
PopulateAssignedCourseData
方法中的代码读取通过所有Course
实体以加载列表,使用视图模型类的课程。对于每个课程,该代码检查教师的Courses
导航属性中是否存在该课程。若要创建高效的查找,检查是否课程指派给指导员时,分配给教师的课程都放入HashSet集合。Assigned
属性设置为true
,课程教师分配。查看将使用此属性来确定哪些复选框必须显示为选中。最后,列表被传递给一个ViewBag
属性中的视图。
接下来,添加用户单击保存时执行的代码。HttpPost
Edit
方法替换以下代码中,调用更新Instructor
实体的Courses
导航属性的新方法。突出显示所做的更改。
[HttpPost] [ValidateAntiForgeryToken] public ActionResult Edit(int id, FormCollection formCollection, string[] selectedCourses) { var instructorToUpdate = db.Instructors .Include(i => i.OfficeAssignment) .Include(i => i.Courses) .Where(i => i.InstructorID == id) .Single(); if (TryUpdateModel(instructorToUpdate, "", new string[] { "LastName", "FirstMidName", "HireDate", "OfficeAssignment" })) { try { if (String.IsNullOrWhiteSpace(instructorToUpdate.OfficeAssignment.Location)) { instructorToUpdate.OfficeAssignment = null; } UpdateInstructorCourses(selectedCourses, instructorToUpdate); db.Entry(instructorToUpdate).State = EntityState.Modified; db.SaveChanges(); return RedirectToAction("Index"); } catch (DataException /* dex */) { //Log the error (uncomment dex variable name after DataException and add a line here to write a log. ModelState.AddModelError("", "Unable to save changes. Try again, and if the problem persists see your system administrator."); } } PopulateAssignedCourseData(instructorToUpdate); return View(instructorToUpdate); } private void UpdateInstructorCourses(string[] selectedCourses, Instructor instructorToUpdate) { if (selectedCourses == null) { instructorToUpdate.Courses = new List<Course>(); return; } var selectedCoursesHS = new HashSet<string>(selectedCourses); var instructorCourses = new