共用方式為


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

在上一個教學課程中,您已顯示相關數據。 在本教學課程中,您將更新相關數據。 針對大部分關聯性,可以藉由更新外鍵字段或導覽屬性來完成。 對於多對多關聯性,Entity Framework 不會直接公開聯結數據表,因此您可以在適當的導覽屬性中新增和移除實體。

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

Course_create_page

Instructor_edit_page_with_courses

講師使用課程編輯

在本教學課程中,您已:

  • 自定義課程頁面
  • 將辦公室新增至講師頁面
  • 將課程新增至講師頁面
  • 更新 DeleteConfirmed
  • 將辦公室位置和課程新增至 [新增] 頁面

必要條件

自定義課程頁面

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

CourseController.cs中,刪除四 Create 個 和 Edit 方法,並以下列程式代碼取代它們:

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

方法HttpGetCreate會呼叫 PopulateDepartmentsDropDownList 方法,而不設定選取的項目,因為對於新課程,尚未建立部門:

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

方法會HttpGetEdit根據已指派給所編輯課程的部門識別碼,設定選取的專案:

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 CreateEdit 的方法也包含程式代碼,這些程式代碼會在錯誤之後重新顯示頁面時設定選取的專案:

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

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

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進行相同的變更。

Scaffolder 通常不會建構主鍵,因為索引鍵值是由資料庫產生,而且無法變更,而且不會對用戶顯示有意義的值。 對於 Course 實體,Scaffolder 確實包含欄位的文字框 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.cshtml 和 Views\Course\Details.cshtml 中,將部門名稱標題從 “Name” 變更為 “Department”,並在 [標題] 字段之前新增課程編號字段。

<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
部門 選取 [ 數學]。

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

執行 [ 編輯 ] 頁面(顯示課程索引頁面,然後按兩下課程上的 [ 編輯 ]。

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

將辦公室新增至講師頁面

當您編輯講師記錄時,您可能會想要更新講師的辦公室指派。 實體 InstructorOfficeAssignment 實體具有一對零或一的關聯性,這表示您必須處理下列情況:

  • 如果使用者清除辦公室工作分派,而且原本有值,您必須移除和刪除 OfficeAssignment 實體。
  • 如果使用者輸入辦公室指派值,而且原本是空的,您必須建立新的 OfficeAssignment 實體。
  • 如果使用者變更辦公室指派的值,您必須變更現有 OfficeAssignment 實體中的值。

開啟 InstructorController.cs 並查看 HttpGet Edit 方法:

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

此處的 Scaffold 程式代碼不是您想要的程式代碼。 它會設定下拉式清單的數據,但您需要的是文本框。 以下列程式代碼取代此方法:

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 方法執行積極式載入,因此 Where 會改用 和 Single 方法來選取講師。

HttpPost Edit以下列程序代碼取代 方法。 處理辦公室指派更新:

[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.Infrastructure

解決重試例外狀況

程式碼會執行下列操作:

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

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

  • 使用從模型繫結器取得的值更新擷取的 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>

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

將課程新增至講師頁面

講師可教授任何數量的課程。 現在,您將藉由新增使用複選框群組來變更課程指派的能力,來增強講師編輯頁面。

Instructor 實體之間的Course關聯性是多對多,這表示您沒有聯結數據表中外鍵屬性的直接存取權。 相反地,您會在導覽屬性中新增 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 集合中。 屬性 Assigned 會針對指派講師的課程設定為 true 。 檢視會使用這個屬性,來判斷哪一個核取方塊必須顯示為已選取。 最後,清單會傳遞至 屬性中的 ViewBag 檢視。

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

[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 變更回 。Edit

由於檢視沒有實體集合 Course ,因此模型系結器無法自動更新 Courses 導覽屬性。 您不會使用模型系結器來更新 Courses 導覽屬性,而是在新的 UpdateInstructorCourses 方法中執行此動作。 因此您必須從模型繫結器中排除 Courses 屬性。 這不需要對呼叫 TryUpdateModel 的程式代碼進行任何變更,因為您使用的是明確的清單多載,而且 Courses 不在 include 清單中。

如果未選取複選框,中的 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 字段,方法是在字段的元素OfficeAssignment和 [儲存] 按鈕的 元素之前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>

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

此程式碼會建立一個 HTML 表格,該表格中有三個資料行。 在每個資料行中,核取方塊的後方會是由課程號碼和標題組成的標題。 複選框全都有相同的名稱(“selectedCourses”),這會通知模型系結器,它們會被視為群組。 value每個複選框的 屬性都會設定為 [張貼頁面時] 的值CourseID.,模型系結器會將數位傳遞至控制器,該控制器只包含CourseID所選取複選框的值。

一開始轉譯複選框時,指派給講師 checked 的課程會有屬性,這會選取它們(顯示已核取這些屬性)。

變更課程指派之後,您會想要在網站返回 Index 頁面時驗證變更。 因此,您必須將數據行新增至該頁面中的數據表。 在此情況下,您不需要使用 ViewBag 對象,因為您想要顯示的信息已經位於 Courses 您傳遞至頁面做為模型的實體導覽屬性 Instructor 中。

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 和不同的更新方法。

更新 DeleteConfirmed

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

此程式代碼類似於您在Edit方法中看到的內容,但一開始未選取任何課程。 方法HttpGetCreate會呼叫 PopulateAssignedCourseData 方法不是因為可能會選取課程,而是為了在檢視中提供迴圈的空白集合foreach(否則檢視程式代碼會擲回 Null 參考例外狀況)。

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 功能教學課程中所述,Entity Framework 預設會隱含實作交易。 如需需要更多控制權的案例,例如,如果您想要在交易中包含 Entity Framework 外部完成的作業,請參閱 在 MSDN 上使用交易

取得程式碼

下載已完成的專案

其他資源

如需其他 Entity Framework 資源的連結,請參閱 ASP.NET 數據存取 - 建議的資源

後續步驟

在本教學課程中,您已:

  • 自定義課程頁面
  • 已將辦公室新增至講師頁面
  • 已將課程新增至講師頁面
  • 已更新DeleteConfirmed
  • 已將辦公室位置和課程新增至 [建立] 頁面

請前進到下一篇文章,以瞭解如何實作異步程序設計模型。