在前面的教程中,您將顯示相關的數據 ;在本教程中,您會更新相關的數據。對於大多數的關系,這個目標是可以通過更新相應的外鍵字段來達到的。對於多對多關系,實體框架並不直接,暴露聯接表,因此您必須顯式添加和刪除,並從相應的導航屬性的實體。
下面的插圖顯示頁面,您將利用工作。


為課程自定義創建和編輯頁面
當創建新的課程實體時,它必須擁有一個關系到現行的部門。為了推動這項工作,搭建的代碼包括控制器方法,並且創建和編輯視圖中包括用於選擇處的下拉列表。下拉列表中設置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的名稱傳遞給DropDownListhelper,和助手就會知道應該命名為DepartmentID的SelectList ViewBag對象中尋找.
HttpGet Create方法調用PopulateDepartmentsDropDownList方法無需設置所選的項目,因為有一個新課程部尚未確立:
public ActionResult Create() { PopulateDepartmentsDropDownList(); return View(); }
HttpGetEdit方法設置所選的項目,基於已分配給課程正在編輯該署的 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 ,然后看看HttpGetEdit方法:
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導航屬性。這是你的所作所為中HttpGetEdit方法相同。 -
從模型聯編程序的值更新檢索到的
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,用下面的代碼替換HttpGetEdit方法。高亮行為所做的更改。
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