教學課程:在 ASP.NET MVC 應用程式中使用 EF 更新相關數據
在上一個教學課程中,您已顯示相關數據。 在本教學課程中,您將更新相關數據。 針對大部分關聯性,可以藉由更新外鍵字段或導覽屬性來完成。 對於多對多關聯性,Entity Framework 不會直接公開聯結數據表,因此您可以在適當的導覽屬性中新增和移除實體。
下列圖例顯示了您將操作的一些頁面。
在本教學課程中,您已:
- 自定義課程頁面
- 將辦公室新增至講師頁面
- 將課程新增至講師頁面
- 更新 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
。
方法HttpGet
Create
會呼叫 PopulateDepartmentsDropDownList
方法,而不設定選取的項目,因為對於新課程,尚未建立部門:
public ActionResult Create()
{
PopulateDepartmentsDropDownList();
return View();
}
方法會HttpGet
Edit
根據已指派給所編輯課程的部門識別碼,設定選取的專案:
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
Create
和 Edit
的方法也包含程式代碼,這些程式代碼會在錯誤之後重新顯示頁面時設定選取的專案:
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。 |
部門 | 選取 [ 數學]。 |
按一下 [建立]。 [課程索引] 頁面隨即顯示,並新增至清單的新課程。 [索引] 頁面中的部門名稱來自於導覽屬性,顯示關聯性已正確建立。
執行 [ 編輯 ] 頁面(顯示課程索引頁面,然後按兩下課程上的 [ 編輯 ]。
變更頁面上的資料,然後按一下 [儲存]。 [課程索引] 頁面會顯示已更新的課程數據。
將辦公室新增至講師頁面
當您編輯講師記錄時,您可能會想要更新講師的辦公室指派。 實體 Instructor
與 OfficeAssignment
實體具有一對零或一的關聯性,這表示您必須處理下列情況:
- 如果使用者清除辦公室工作分派,而且原本有值,您必須移除和刪除
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 中,將方法取代HttpGet
Edit
為下列程序代碼。 所做的變更已醒目提示。
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);
}
}
}
}
方法簽章現在與 方法不同HttpGet
Edit
,因此方法名稱會從 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 中移除 HttpGet
和 HttpPost
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方法中看到的內容,但一開始未選取任何課程。 方法HttpGet
Create
會呼叫 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
- 已將辦公室位置和課程新增至 [建立] 頁面
請前進到下一篇文章,以瞭解如何實作異步程序設計模型。
意見反應
https://aka.ms/ContentUserFeedback。
即將登場:在 2024 年,我們將逐步淘汰 GitHub 問題作為內容的意見反應機制,並將它取代為新的意見反應系統。 如需詳細資訊,請參閱:提交並檢視相關的意見反應