Share via


教學課程:在 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 具名 DepartmentID 的 物件 SelectList

方法會 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);
}

HttpPostEdit 的方法 Create 也包含程式碼,這些程式碼會在錯誤之後重新顯示頁面時設定選取的專案:

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

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

課程檢視已使用部門欄位的下拉式清單進行 Scaffold,但您不希望此欄位的 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.cshtmlViews\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
部門 選取[數學]。

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

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

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

將辦公室新增至講師頁面

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

  • 如果使用者清除辦公室工作分派,且原先有值,您必須移除和刪除 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);
}

此處的 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 方法來選取講師。

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

解決重試例外狀況

此程式碼會執行下列動作:

  • 將方法名稱變更為 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>

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

將課程新增至講師頁面

講師可教授任何數量的課程。 現在,您將新增使用一組核取方塊來變更課程指派的能力,來增強 Instructor 編輯頁面。

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 不在包含清單中。

如果未選取任何核取方塊,中的 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 下列程式碼緊接在欄位的元素 OfficeAssignment 後面,以及 [儲存] 按鈕的 元素之前 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 物件,因為您想要顯示的資訊已經在您傳遞至頁面做為模型的實體導覽屬性 InstructorCourses

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>

執行 Instructor Index 頁面,以查看指派給每個講師的課程。

按一下講師上的 [編輯 ] 以查看 [編輯] 頁面。

變更一些課程指派,然後按一下 [ 儲存]。 您所做的變更會反映在 [索引] 頁面上。

注意:這裡用來編輯講師課程資料的方法在課程數量有限時運作良好。 針對更大的集合,將需要不同的 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 中,刪除 HttpGetHttpPostCreate 方法,然後在其位置新增下列程式碼:

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 上使用 交易

取得程式碼

下載已完成的專案

其他資源

您可以在 ASP.NET 資料存取 - 建議的資源中找到其他 Entity Framework 資源的連結。

後續步驟

在本教學課程中,您:

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

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