共用方式為


教學課程:在 ASP.NET MVC 應用程式中使用 EF 更新相關資料

在上一個教學課程中您顯示了相關資料。 在本教學課程中,您將更新相關資料。 對於大多數關係,這可以透過更新外鍵欄位或導覽屬性來完成。 對於多對多關係,實體框架不會直接公開聯接表,因此您可以在適當的導覽屬性中新增和刪除實體。

下列圖例顯示了您將操作的一些頁面。

Course_create_page

Instructor_edit_page_with_courses

講師編輯課程

在本教學課程中,您已:

  • 自訂課程頁面
  • 將 Office 新增至講師頁面
  • 將課程新增至講師頁面
  • 更新刪除已確認
  • 將辦公室位置和課程新增至 [新增] 頁面

必要條件

自訂課程頁面

當新的課程實體建立時,其必須要與現有的部門具有關聯性。 若要達成此目的,Scaffold 程式碼包含了控制器方法和 [建立] 和 [編輯] 檢視,當中包含了一個可選取部門的下拉式清單。 下拉式清單會設定 Course.DepartmentID 外部索引鍵屬性,以讓 Entity Framework 使用適當的 Department 實體載入 Department 導覽屬性。 您將使用 Scaffold 程式碼,但會稍微對其進行一些變更以新增錯誤處理及排序下拉式清單。

CourseController.cs 中,刪除四個 CreateEdit 方法並用以下程式碼取代它們:

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 (RetryLimitExceededException /* dex */)
    {
        //Log the error (uncomment dex variable name 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)
{
    if (id == null)
    {
        return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
    }
    Course course = db.Courses.Find(id);
    if (course == null)
    {
        return HttpNotFound();
    }
    PopulateDepartmentsDropDownList(course.DepartmentID);
    return View(course);
}

[HttpPost, ActionName("Edit")]
[ValidateAntiForgeryToken]
public ActionResult EditPost(int? id)
{
    if (id == null)
    {
        return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
    }
    var courseToUpdate = db.Courses.Find(id);
    if (TryUpdateModel(courseToUpdate, "",
       new string[] { "Title", "Credits", "DepartmentID" }))
    {
        try
        {
            db.SaveChanges();

            return RedirectToAction("Index");
        }
        catch (RetryLimitExceededException /* dex */)
        {
            //Log the error (uncomment dex variable name 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(courseToUpdate.DepartmentID);
    return View(courseToUpdate);
}

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);
} 

在文件開頭加入以下 using 語句:

using System.Data.Entity.Infrastructure;

PopulateDepartmentsDropDownList 方法取得按名稱排序的所有部門的列表,建立下拉列表的 SelectList 集合,並將該集合傳遞到 ViewBag 屬性中的檢視。 方法接受選擇性的 selectedDepartment 參數,可允許呼叫程式碼在呈現下拉式清單時指定選取的項目。 檢視會將名稱 DepartmentID 傳遞給 DropDownList 幫助程序,然後該幫助程序就會知道在 ViewBag 物件中尋找名為 DepartmentIDSelectList

HttpGet 方法在呼叫 Create 方法時沒有設定所選項目,因為對於新課程,PopulateDepartmentsDropDownList 部門尚未建立:

public ActionResult Create()
{
    PopulateDepartmentsDropDownList();
    return View();
}

HttpGetEdit 方法根據已指派給正在編輯的課程的部門 ID 設定所選項目:

public ActionResult Edit(int? id)
{
    if (id == null)
    {
        return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
    }
    Course course = db.Courses.Find(id);
    if (course == null)
    {
        return HttpNotFound();
    }
    PopulateDepartmentsDropDownList(course.DepartmentID);
    return View(course);
}

CreateEdit 兩者的 HttpPost 方法還包括在錯誤後重新顯示頁面時設定所選目的代碼:

catch (RetryLimitExceededException /* dex */)
{
    //Log the error (uncomment dex variable name 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);

此程式碼可確保當重新顯示頁面以顯示錯誤訊息時,選取的任何部門都會保持選取狀態。

課程檢視已經使用部門欄位的下拉清單進行了搭建,但您不希望該欄位有 DepartmentID 標題,因此對 Views\Course\Create.cshtml 檔案進行以下突出顯示的變更以變更標題。

@model ContosoUniversity.Models.Course

@{
    ViewBag.Title = "Create";
}

<h2>Create</h2>

@using (Html.BeginForm()) 
{
    @Html.AntiForgeryToken()
    
    <div class="form-horizontal">
        <h4>Course</h4>
        <hr />
        @Html.ValidationSummary(true)

        <div class="form-group">
            @Html.LabelFor(model => model.CourseID, new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.EditorFor(model => model.CourseID)
                @Html.ValidationMessageFor(model => model.CourseID)
            </div>
        </div>

        <div class="form-group">
            @Html.LabelFor(model => model.Title, new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.EditorFor(model => model.Title)
                @Html.ValidationMessageFor(model => model.Title)
            </div>
        </div>

        <div class="form-group">
            @Html.LabelFor(model => model.Credits, new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.EditorFor(model => model.Credits)
                @Html.ValidationMessageFor(model => model.Credits)
            </div>
        </div>

        <div class="form-group">
            <label class="control-label col-md-2" for="DepartmentID">Department</label>
            <div class="col-md-10">
                @Html.DropDownList("DepartmentID", String.Empty)
                @Html.ValidationMessageFor(model => model.DepartmentID)
            </div>
        </div>

        <div class="form-group">
            <div class="col-md-offset-2 col-md-10">
                <input type="submit" value="Create" class="btn btn-default" />
            </div>
        </div>
    </div>
}

<div>
    @Html.ActionLink("Back to List", "Index")
</div>

@section Scripts {
    @Scripts.Render("~/bundles/jqueryval")
}

Views\Course\Edit.cshtml 中進行相同的變更。

通常,支架不會建立主鍵,因為鍵值是由資料庫產生的,無法更改,也不是向使用者顯示的有意義的值。 對於課程實體,支架確實包含 CourseID 欄位的文字框,因為它知道該 DatabaseGeneratedOption.None 屬性意味著使用者應該能夠輸入主鍵值。 但它不明白,因為這個數字是有意義的,你想在其他檢視中看到它,所以你需要手動添加它。

Views\Course\Edit.cshtml 中,在標題欄位之前新增一個課程編號欄位。 因為它是主鍵,所以會顯示,但無法更改。

<div class="form-group">
    @Html.LabelFor(model => model.CourseID, new { @class = "control-label col-md-2" })
    <div class="col-md-10">
        @Html.DisplayFor(model => model.CourseID)
    </div>
</div>

編輯檢視中已經有一個課程編號的隱藏欄位 (Html.HiddenFor 幫助程式)。 新增 Html.LabelFor 幫助程式並不能消除對隱藏欄位的需要,因為當使用者點擊「編輯」頁面上的儲存時,它不會導致課程編號包含在發佈的資料中。

Views\Course\Delete.cshtmlViews\Course\Details.cshtml 中,將部門名稱標題從 Name 變更為 Department,並在 Title 欄位之前新增課程號碼欄位。

<dt>
    Department
</dt>

<dd>
    @Html.DisplayFor(model => model.Department.Name)
</dd>

<dt>
    @Html.DisplayNameFor(model => model.CourseID)
</dt>

<dd>
    @Html.DisplayFor(model => model.CourseID)
</dd>

執行建立頁面 (顯示課程索引頁面並點擊新建) 並輸入新課程的資料:

設定
數字 輸入 1000
標題 輸入代數
學分 輸入 4
部門 選擇數學

按一下 [建立]。 將顯示「課程索引」頁面,並將新課程新增至清單。 [索引] 頁面中的部門名稱來自於導覽屬性,顯示關聯性已正確建立。

執行編輯頁面 (顯示課程索引頁面並點擊課程上的編輯)。

變更頁面上的資料,然後按一下 [儲存]。 課程索引頁面顯示更新的課程資料。

將 Office 新增至講師頁面

當您編輯講師記錄時,您可能會想要更新講師的辦公室指派。 Instructor 實體與 OfficeAssignmentBot 控制代碼 實體具有一對零或一的關係,這意味著您必須處理以下情況:

  • 如果使用者清除了辦公室分配並且它最初具有值,則您必須移除並刪除 OfficeAssignment 實體。
  • 如果使用者輸入辦公室分配值且該值最初為空,則您必須建立一個新 OfficeAssignment 實體。
  • 如果使用者變更辦公室指派的值,您必須變更現有 OfficeAssignment 實體中的值。

打開 InstructorController.cs,看一下 HttpGetEdit 方法:

{
    if (id == null)
    {
        return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
    }
    Instructor instructor = db.Instructors.Find(id);
    if (instructor == null)
    {
        return HttpNotFound();
    }
    ViewBag.ID = new SelectList(db.OfficeAssignments, "InstructorID", "Location", instructor.ID);
    return View(instructor);
}

這裡的支架程式碼不是您想要的。 它正在為下拉列表設定資料,但您需要的是一個文字方塊。 將此方法替換為以下程式碼:

public ActionResult Edit(int? id)
{
    if (id == null)
    {
        return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
    }
    Instructor instructor = db.Instructors
        .Include(i => i.OfficeAssignment)
        .Where(i => i.ID == id)
        .Single();
    if (instructor == null)
    {
        return HttpNotFound();
    }
    return View(instructor);
}

此程式碼刪除 ViewBag 語句並新增關聯 OfficeAssignment 實體的預先載入。 您無法使用 Find 方法執行預先加載,因此使用 WhereSingle 方法來選擇講師。

HttpPostEdit 方法替換為以下程式碼。 它處理辦公室分配更新:

[HttpPost, ActionName("Edit")]
[ValidateAntiForgeryToken]
public ActionResult EditPost(int? id)
{
    if (id == null)
    {
        return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
    }
    var instructorToUpdate = db.Instructors
       .Include(i => i.OfficeAssignment)
       .Where(i => i.ID == id)
       .Single();

    if (TryUpdateModel(instructorToUpdate, "",
       new string[] { "LastName", "FirstMidName", "HireDate", "OfficeAssignment" }))
    {
       try
       {
          if (String.IsNullOrWhiteSpace(instructorToUpdate.OfficeAssignment.Location))
          {
             instructorToUpdate.OfficeAssignment = null;
          }

          db.SaveChanges();

          return RedirectToAction("Index");
       }
       catch (RetryLimitExceededException /* dex */)
      {
         //Log the error (uncomment dex variable name 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.");
      }
   }
   return View(instructorToUpdate);
}

引用 RetryLimitExceededException 需要 using 聲明;添加它 - 將滑鼠懸停在 RetryLimitExceededException 上。 將出現以下訊息:重試例外狀況訊息

選擇顯示潛在修復,然後使用 System.Data.Entity.Infrastruct

解決重試例外狀況

程式碼會執行下列操作:

  • 將方法名稱變更為 EditPost,因為簽名現在與 HttpGet 方法相同 (ActionName 屬性指定仍使用 /Edit/ URL)。

  • 針對 OfficeAssignment 導覽屬性使用積極式載入從資料庫中取得目前的 Instructor 實體。 這與您在 HttpGetEdit 方法中所做的相同。

  • 使用從模型繫結器取得的值更新擷取的 Instructor 實體。 使用的 TryUpdateModel 重載可讓您列出要包含的屬性。 這可防止大量指派,如同在第二個教學課程中所解釋的。

    if (TryUpdateModel(instructorToUpdate, "",
          new string[] { "LastName", "FirstMidName", "HireDate", "OfficeAssignment" }))
    
  • 如果辦公室位置空白,請將 Instructor.OfficeAssignment 屬性設定為 null,以便刪除 OfficeAssignment 資料表中的相關資料列。

    if (String.IsNullOrWhiteSpace(instructorToUpdate.OfficeAssignment.Location))
    {
        instructorToUpdate.OfficeAssignment = null;
    }
    
  • 將變更儲存到資料庫。

Views\Instructor\Edit.cshtml 中,在div僱用日期欄位的元素後面,新增一個用於編輯辦公地點的新欄位:

<div class="form-group">
    @Html.LabelFor(model => model.OfficeAssignment.Location, new { @class = "control-label col-md-2" })
    <div class="col-md-10">
        @Html.EditorFor(model => model.OfficeAssignment.Location)
        @Html.ValidationMessageFor(model => model.OfficeAssignment.Location)
    </div>
</div>

運行該頁面 (選擇講師索引標籤,然後按一下講師上的編輯)。 變更 [辦公室位置],然後按一下 [儲存]

將課程新增至講師頁面

講師可教授任何數量的課程。 現在,您將透過新增使用一組複選框更改課程作業的功能來增強教師編輯頁面。

CourseInstructor 實體之間的關係是多對多,這表示您無法直接存取連線表中的外鍵屬性。 相反,您可以在 Instructor.Courses 導航屬性中新增和刪除實體。

可讓您變更講師指派之課程的 UI 為一組核取方塊。 資料庫中每個課程的核取方塊都會顯示,而該名講師目前受指派的課程會已選取狀態顯示。 使用者可選取或清除核取方塊來變更課程指派。 如果課程數量更多,您可能想要使用不同的方法在檢視中呈現資料,但您會使用相同的操作導航屬性的方法來建立或刪除關係。

若要針對核取方塊清單提供資料給檢視,您必須使用一個檢視模型類別。 在 ViewModels 資料夾中建立 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)
{
    if (id == null)
    {
        return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
    }
    Instructor instructor = db.Instructors
        .Include(i => i.OfficeAssignment)
        .Include(i => i.Courses)
        .Where(i => i.ID == id)
        .Single();
    if (instructor == null)
    {
        return HttpNotFound();
    }
    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 導覽屬性新增積極式載入,然後使用 AssignedCourseData 檢視模型類別來呼叫新的 PopulateAssignedCourseData 方法以提供資訊給核取方塊陣列。

PopulateAssignedCourseData 方法中的程式碼會讀取所有的 Course 實體以使用檢視模型類別載入課程清單。 針對每個課程,程式碼會檢查課程是否存在於講師的 Courses 導覽屬性中。 為了在檢查課程是否分配給教師時建立有效的查找,分配給教師的課程被放入 HashSet 集合中。 此 Assignedtrue 屬性設定為教師分配的課程。 檢視會使用這個屬性,來判斷哪一個核取方塊必須顯示為已選取。 最後,列表被傳遞到 ViewBag 屬性中的檢視。

接下來,新增當使用者按一下 [儲存] 時要執行的程式碼。 使用以下程式碼取代 EditPost 方法,該程式碼呼叫更新 Instructor 實體的 Courses 導航屬性的新方法。 所做的變更已醒目提示。

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(int? id, string[] selectedCourses)
{
    if (id == null)
    {
        return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
    }
    var instructorToUpdate = db.Instructors
       .Include(i => i.OfficeAssignment)
       .Include(i => i.Courses)
       .Where(i => i.ID == 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.SaveChanges();

            return RedirectToAction("Index");
        }
        catch (RetryLimitExceededException /* dex */)
        {
            //Log the error (uncomment dex variable name 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 HashSet<int>
       (instructorToUpdate.Courses.Select(c => c.CourseID));
   foreach (var course in db.Courses)
   {
      if (selectedCoursesHS.Contains(course.CourseID.ToString()))
      {
         if (!instructorCourses.Contains(course.CourseID))
         {
            instructorToUpdate.Courses.Add(course);
         }
      }
      else
      {
         if (instructorCourses.Contains(course.CourseID))
         {
            instructorToUpdate.Courses.Remove(course);
         }
      }
   }
}

HttpGetEdit 方法簽名現在與方法不同,因此方法名稱從 EditPost back 更改為 Edit

由於檢視沒有 Course 實體集合,因此模型綁定器無法自動更新 Courses 導航屬性。 您將在 UpdateInstructorCourses 方法中執行此操作,而不是使用模型綁定器來更新 Courses 導航屬性。 因此您必須從模型繫結器中排除 Courses 屬性。 這不需要對呼叫 TryUpdateModel 的程式碼進行任何更改,因為您正在使用明確列表重載並且 Courses 不在包含列表中。

如果未選取任何複選框,則 UpdateInstructorCourses 中的程式碼將使用空集合初始化 Courses 導覽屬性:

if (selectedCourses == null)
{
    instructorToUpdate.Courses = new List<Course>();
    return;
}

程式碼會執行迴圈,尋訪資料庫中所有的課程,並檢查每個已指派給講師的課程,以及在檢視中選取的課程。 為了協助達成有效率的搜尋,後者的兩個集合會儲存在 HashSet 物件中。

若課程的核取方塊已被選取,但課程並未位於 Instructor.Courses 導覽屬性中,則課程便會新增至導覽屬性的集合中。

if (selectedCoursesHS.Contains(course.CourseID.ToString()))
{
    if (!instructorCourses.Contains(course.CourseID))
    {
        instructorToUpdate.Courses.Add(course);
    }
}

若課程的核取方塊未被選取,但課程卻位於 Instructor.Courses 導覽屬性中,則課程便會從導覽屬性的集合中移除。

else
{
    if (instructorCourses.Contains(course.CourseID))
    {
        instructorToUpdate.Courses.Remove(course);
    }
}

Views\Instructor\Edit.cshtml 中,新增一個帶有複選框數組的 Courses 欄位,div 方法是在欄位元素之後 OfficeAssignmentdiv保存按鈕元素之前添加以下程式碼:

<div class="form-group">
    <div class="col-md-offset-2 col-md-10">
        <table>
            <tr>
                @{
                    int cnt = 0;
                    List<ContosoUniversity.ViewModels.AssignedCourseData> courses = ViewBag.Courses;

                    foreach (var course in courses)
                    {
                        if (cnt++ % 3 == 0)
                        {
                            @:</tr><tr>
                        }
                        @:<td>
                            <input type="checkbox"
                               name="selectedCourses"
                               value="@course.CourseID"
                               @(Html.Raw(course.Assigned ? "checked=\"checked\"" : "")) />
                               @course.CourseID @:  @course.Title
                        @:</td>
                    }
                    @:</tr>
                }
        </table>
    </div>
</div>

貼上程式碼後,如果換行符和縮排與此處不同,請手動修復所有內容,使其看起來像您在此處看到的那樣。 縮排不一定要是完美的,但 @</tr><tr>@:<td>@:</td>@</tr> 必須要如顯示般各自在獨立的一行上,否則您會接收到執行階段錯誤。

此程式碼會建立一個 HTML 表格,該表格中有三個資料行。 在每個資料行中,核取方塊的後方會是由課程號碼和標題組成的標題。 這些複選框都具有相同的名稱 (“selectedCourses”),這通知模型綁定器它們將被視為一個群組。 每個複選框的 value 屬性設定為CourseID.當頁面發佈時的 CourseID 值,模型綁定器將一個陣列傳遞給控制器,該陣列僅包含所選複選框的值。

當複選框最初呈現時,分配給教師的課程的複選框具有 checked 屬性,可以選擇它們 (顯示它們已選中)。

更改課程作業後,您將希望能夠在網站返回 Index 頁面時驗證變更。 因此,您需要在該頁面的表中新增一列。 在這種情況下,您不需要使用該 ViewBag 物件,因為您要顯示的資訊已經位於您作為模型傳遞到頁面的 Instructor 實體的 Courses 導航屬性中。

Views\Instructor\Index.cshtml 中,緊跟著在 Office 標題後面新增一個 Courses 標題,如下例所示:

<tr> 
    <th>Last Name</th> 
    <th>First Name</th> 
    <th>Hire Date</th> 
    <th>Office</th>
    <th>Courses</th>
    <th></th> 
</tr>

然後在辦公室位置詳細資料儲存格後面新增一個新的詳細資料儲存格:

<td>
    @if (item.OfficeAssignment != null)
    {
        @item.OfficeAssignment.Location
    }
</td>
<td>
    @{
        foreach (var course in item.Courses)
        {
            @course.CourseID @:  @course.Title <br />
        }
    }
</td>
<td>
    @Html.ActionLink("Select", "Index", new { id = item.ID }) |
    @Html.ActionLink("Edit", "Edit", new { id = item.ID }) |
    @Html.ActionLink("Details", "Details", new { id = item.ID }) |
    @Html.ActionLink("Delete", "Delete", new { id = item.ID })
</td>

運行講師索引頁面可查看指派給每位講師的課程。

點擊講師上的編輯以查看編輯頁面。

更改一些課程作業並點擊儲存。 您所做的變更會反映在 [索引] 頁面上。

注意:當課程數量有限時,此處用於編輯講師課程資料的方法效果很好。 針對更大的集合,將需要不同的 UI 和不同的更新方法。

更新刪除已確認

InstructorController.cs 中,刪除 DeleteConfirmed 方法並在其位置插入以下程式碼。

[HttpPost, ActionName("Delete")]
[ValidateAntiForgeryToken]
public ActionResult DeleteConfirmed(int id)
{
   Instructor instructor = db.Instructors
     .Include(i => i.OfficeAssignment)
     .Where(i => i.ID == id)
     .Single();

   db.Instructors.Remove(instructor);

    var department = db.Departments
        .Where(d => d.InstructorID == id)
        .SingleOrDefault();
    if (department != null)
    {
        department.InstructorID = null;
    }

   db.SaveChanges();
   return RedirectToAction("Index");
}

此程式碼進行了以下更改:

  • 如果講師被指派為任何部門的管理員,請從該部門刪除講師指派。 如果沒有此程式碼,如果您嘗試刪除被指定為部門管理員的講師,您將收到引用完整性錯誤。

此代碼不處理將一名講師指定為多個部門的管理員的情況。 在上一個教學課程中,您將添加程式碼來防止這種情況發生。

將辦公室位置和課程新增至 [新增] 頁面

InstructorController.cs 中,刪除 HttpGetHttpPost Create 方法,然後在其位置新增以下程式碼:

public ActionResult Create()
{
    var instructor = new Instructor();
    instructor.Courses = new List<Course>();
    PopulateAssignedCourseData(instructor);
    return View();
}

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "LastName,FirstMidName,HireDate,OfficeAssignment" )]Instructor instructor, string[] selectedCourses)
{
    if (selectedCourses != null)
    {
        instructor.Courses = new List<Course>();
        foreach (var course in selectedCourses)
        {
            var courseToAdd = db.Courses.Find(int.Parse(course));
            instructor.Courses.Add(courseToAdd);
        }
    }
    if (ModelState.IsValid)
    {
        db.Instructors.Add(instructor);
        db.SaveChanges();
        return RedirectToAction("Index");
    }
    PopulateAssignedCourseData(instructor);
    return View(instructor);
}

此程式碼與您在編輯方法中看到的程式碼類似,但最初沒有選擇任何課程。 此方法呼叫 HttpGetCreatePopulateAssignedCourseData 方法並不是因為可能選擇了課程,而是為了為檢視中的 foreach 循環提供一個空集合 (否則檢視程式碼將拋出空引用例外狀況)。

HttpPost Create 方法將每個選定的課程新增至 Courses 導航屬性中,然後檢查驗證錯誤並將新講師新增至資料庫的範本程式碼之前。 即使存在模型錯誤,也會新增課程,以便當存在模型錯誤時 (例如,使用者輸入了無效日期),以便當頁面重新顯示並顯示錯誤訊息時,所做的任何課程選擇都會自動恢復。

請注意,為了要能夠將課程新增到 Courses 導覽屬性,您必須將屬性以空集合初始化:

instructor.Courses = new List<Course>();

作為在控制器程式碼中完成這項操作的替代方案,您可以在 Instructor 模型中藉由將屬性 getter 變更為在不存在時自動建立集合來完成,如以下範例所示:

private ICollection<Course> _courses;
public virtual ICollection<Course> Courses 
{ 
    get
    {
        return _courses ?? (_courses = new List<Course>());
    }
    set
    {
        _courses = value;
    } 
}

若您使用這種方式修改了 Courses 屬性,您便可以移除控制器中的明確屬性初始化程式碼。

Views\Instructor\Create.cshtml 中,在僱用日期欄位之後和提交按鈕之前新增辦公室位置文字方塊和課程複選框。

<div class="form-group">
    @Html.LabelFor(model => model.OfficeAssignment.Location, new { @class = "control-label col-md-2" })
    <div class="col-md-10">
        @Html.EditorFor(model => model.OfficeAssignment.Location)
        @Html.ValidationMessageFor(model => model.OfficeAssignment.Location)
    </div>
</div>

<div class="form-group">
    <div class="col-md-offset-2 col-md-10">
        <table>
            <tr>
                @{
                    int cnt = 0;
                    List<ContosoUniversity.ViewModels.AssignedCourseData> courses = ViewBag.Courses;

                    foreach (var course in courses)
                    {
                        if (cnt++ % 3 == 0)
                        {
                            @:</tr><tr>
                        }
                        @:<td>
                            <input type="checkbox"
                               name="selectedCourses"
                               value="@course.CourseID"
                               @(Html.Raw(course.Assigned ? "checked=\"checked\"" : "")) />
                               @course.CourseID @:  @course.Title
                        @:</td>
                    }
                    @:</tr>
                }
        </table>
    </div>
</div>

貼上程式碼後,像之前在「編輯」頁面中所做的那樣修復換行符和縮排。

運行建立頁面並新增講師。

處理交易

基本 CRUD 功能教學課程中所述,預設情況下,實體框架隱式實作事務。 對於需要更多控制的場景 (例如,如果您想要在交易中包含在實體框架之外完成的操作),請參閱 MSDN 上的使用事務。

取得程式碼

下載已完成的項目

其他資源

可以在 ASP.NET 資料存取 - 推薦資源中找到其他實體框架資源的連結。

後續步驟

在本教學課程中,您已:

  • 客製化課程頁面
  • 將 Office 新增至講師頁面
  • 將課程新增至講師頁面
  • 更新刪除已確認
  • 建立頁面新增辦公室地點和課程

繼續閱讀下一篇文章,了解如何實作非同步程式設計模型。