[渣譯文] 使用 MVC 5 的 EF6 Code First 入門 系列:爲ASP.NET MVC應用程序更新相關數據

這是微軟官方教程Getting Started with Entity Framework 6 Code First using MVC 5 系列的翻譯,這裏是第八篇:爲ASP.NET MVC應用程序更新相關數據css

原文:html

譯文版權全部,謝絕全文轉載——但您能夠在您的網站上添加到該教程的連接。jquery

在以前的教程中您已經成功顯示了相關數據。在本教程中你將學習如何對相關數據進行更新。對於大多數關係,能夠從主鍵或者導航屬性來進行更新。對於多對多關係,實體框架不會直接公開鏈接表,因此你能夠從相應的導航屬性添加和移除實體。程序員

下面的截圖顯示了你將要實現的頁面。數據庫

爲課程自定義建立和編輯頁

當建立新的課程實體時,他必須擁有一個和已存在系的關係。爲此,腳手架代碼建立的控制器方法及新建和編輯視圖種豆包含了用於選擇系的下拉列表。下拉列表用來設置Course.DepartmentID外鍵屬性,這對於實體框架經過Department導航屬性來加載Department實體是必須的。你將使用腳手架代碼,但須要對其作一些小的改動來增長錯誤處理和對列表內容進行排序。bootstrap

在Coursecontroller.cs中,刪除以前的Create和Edit方法,並添加下面的代碼:數組

        private void PopulateDepartmentsDropDownList(object selectedDrpaerment = null)
        {
            var departmentsQuery = from d in db.Departments
                                   orderby d.Name
                                   select d;
            ViewBag.DepartmentID = new SelectList(departmentsQuery, "DepartmentID", "Name", selectedDrpaerment);
        }

        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)
            {
                ModelState.AddModelError("", "沒法保存數據,請重試或聯繫管理員。");
            }
            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,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 (RetryLimitExceededException)
            {
                ModelState.AddModelError("", "沒法保存更改,請重試或聯繫管理員。");
            }
            PopulateDepartmentsDropDownList(course.DepartmentID);
            return View(course);
        }

在文件的開頭增長如下引用:服務器

using System.Data.Entity.Infrastructure;

PopulateDepartmentsDropDownList方法獲取全部的系列表並按照名稱進行排序來建立一個下拉列表。並經過ViewBag屬性傳遞到視圖上。該方法接收一個可選參數selectedDepartment,在下拉列表渲染時容許調用代碼指定被選擇的項目。視圖將傳遞DepartmentID名稱給下拉列表幫助器,而後幫助器知道應當使用DepartmentID名來在ViewBag中對象進行下拉列表的查找。架構

HttpGet Create方法調用PopulateDepartmentsDropDownList方法,但並不設置已選項目,由於對於一個新的課程來講,還沒有肯定其所屬的系。mvc

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

Create和Edit的HttpPost方法還包括當出現了錯誤後,從新顯示頁面時要再設置一次所選項目的代碼:

            catch (RetryLimitExceededException)
            {
                ModelState.AddModelError("", "沒法保存更改,請重試或聯繫管理員。");
            }
            PopulateDepartmentsDropDownList(course.DepartmentID);
            return View(course);

這段代碼確保當頁面從新顯示錯誤信息時,已經被選擇的系保持被選擇狀態。

Course視圖已經基於系字段來使用腳手架構建了一個下拉列表。但你並不想使用系ID來做爲標題,因此在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")
}

 以後在Edit視圖中進行相同的更改。

 一般腳手架不會使用主鍵來生成字段,由於主鍵值是由數據庫生成的,沒法更改且對用戶顯示也沒有意義。對於課程實體腳手架代碼包含了一個用於CourseID的文本框,由於DatabaseGeneratedOption.None特性意味着用戶應當能夠輸入主鍵值。但它並不明白由於該號碼只有在你想要讓其顯示在某些特定視圖中才是有意義的。因此您須要手動添加它。

在Edit視圖中,在標題字段以前添加課程編號字段。

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

 

Edit視圖中已經有一個課程編號的隱藏字段(Html.HiddenFor幫助器)。爲隱藏字段添加一個Html.LabelFor幫助器是不必的。由於它不會致使當用戶點擊保存時將課程編號包含在要發送的數據中。

在Delete和Details視圖中,更改系名稱的標題從"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>
        <dt> @Html.DisplayNameFor(model => model.Title) </dt>

 

 

勘誤注意:

以前的Layout頁面由於疏忽路由參數寫錯了,請使用下面的代碼替換佈局頁面。

<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>@ViewBag.Title - Contoso 大學</title>
    @Styles.Render("~/Content/css")
    @Scripts.Render("~/bundles/modernizr")
</head>
<body>
    <div class="navbar navbar-inverse navbar-fixed-top">
        <div class="container">
            <div class="navbar-header">
                <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                </button>
                @Html.ActionLink("Contoso 大學", "Index", "Home", null, new { @class = "navbar-brand" })
            </div>
            <div class="navbar-collapse collapse">
                <ul class="nav navbar-nav">
                    <li>@Html.ActionLink("主頁", "Index", "Home")</li>
                    <li>@Html.ActionLink("關於", "About", "Home")</li>
                    <li>@Html.ActionLink("學生", "Index", "Student")</li>
                    <li>@Html.ActionLink("教師", "Index", "Instructor")</li>
                    <li>@Html.ActionLink("課程", "Index", "Course")</li>
                    <li>@Html.ActionLink("系", "Index", "Department")</li>
                </ul>
            </div>
        </div>
    </div>
    <div class="container body-content">
        @RenderBody()
        <hr />
        <footer>
            <p>&copy; @DateTime.Now.Year - Contoso 大學</p>
        </footer>
    </div>

    @Scripts.Render("~/bundles/jquery")
    @Scripts.Render("~/bundles/bootstrap")
    @RenderSection("scripts", required: false)
</body>
</html>
View Code

 

以及Course的模型, public int CourseID { get; set; }的Display特性應爲[Display(Name = "編號")]

若是你在看到下記日期以前就跟隨教程進行了演練,請將上面兩點更正,謝謝。

2014-5-12

運行應用程序,打開課程的建立頁面(顯示課程索引頁面並單擊建立新的)並輸入新課程的數據: 

單擊建立,課程索引頁會顯示你剛纔新建的課程。同時索引頁面的洗名稱是來自導航屬性的,表示關係已經正確創建。

 

點擊編輯超連接來運行編輯頁。

 更改頁面上的數據並保存,檢查數據是否被正確地保存並顯示。

爲講師添加編輯頁面

當您編輯一名講師的記錄時,你但願可以更新講師的辦公室分配狀況。講師實體和辦公室分配實體之間有一個一到零或一的關係。這意味着您必須處理下列狀況:

  • 若是用戶清除了辦公室分配狀況而且講師原來擁有一個,您必須移除並刪除這個OfficeAssignment實體。
  • 若是用戶輸入了一個辦公室而且原來說師並無分配,您必須新建一個OfficeAssignment實體。
  • 若是用戶更改辦公室分配值,你必須更改已經存在的OfficeAssignment實體。

打開InstructorController.cs,檢查Edit的HttpGet 方法:

        public ActionResult Edit(int? id)
        {
            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方法上使用預先加載。因此這裏使用了Where和Single方法來選擇講師。

下面的代碼替換HttpPost的Edit方法。用來處理辦公室分配更新:

        [HttpPost, ValidateAntiForgeryToken, ActionName("Edit")]
        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.Entry(instructorToUpdate).State = EntityState.Modified;
                    db.SaveChanges();
                    return RedirectToAction("Index");
                }
                catch (RetryLimitExceededException)
                {
                    ModelState.AddModelError("", "沒法保存更改,請重試或聯繫管理員");
                }
            }
            return View(instructorToUpdate);
        }

 

而後添加下列引用:

using System.Data.Entity.Infrastructure;

 

這段代碼執行了如下操做:

  • 將方法名稱變動爲EditPost由於簽名如今和HttpGet方法的同樣。(依然使用ActionName特性指定的Edit URL)
  • 使用延遲加載來從數據庫中獲取當前講師實體的OfficeAssignment導航屬性。和你在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;
    }
  • 將所作的更改保存到數據庫中。

在Edit視圖中,在僱傭日期字段的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>

 

 運行該頁面(選擇教師選項卡,而後點擊編輯講師),更改辦公室位置並保存。

爲教師編輯頁面添加課程分配

教師可以教授任意數量的課程。如今您會經過使用一組複選框來添加更改課程分配的功能,以下所示:

Course和Instructor實體之間的關係是多對多,這意味着您不須要直接訪問鏈接表中的外鍵屬性。相反,你能夠從Istructor.Courses導航屬性中添加和移除實體。

UI使您可以更改使用一組複選框來表示哪些課程是已經分配給教師的。在數據庫中的每一門課程都使用一個複選框來顯示,包括哪些已經分配給教師的。用戶能夠經過選擇或清除複選框來更改課程分配。若是課程數目太多,你可能想要在視圖中使用不一樣的顯示數據的方法,但你會用一樣的方法來操做導航屬性以建立或刪除關係。

爲了給視圖提供複選框的列表,您會使用ViewModel類,在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();
            PopulateAssignedCourseData(instructor);
            if (instructor == null)
            {
                return HttpNotFound();
            }
            return View(instructor);
        }

 private void PopulateAssignedCourseData(Instructor instructor) { var allCourse = db.Courses; var instructorCourses = new HashSet<int>(instructor.Courses.Select(c => c.CourseID)); var viewModel = new List<AssignedCourseData>(); foreach (var course in allCourse) { viewModel.Add(new AssignedCourseData { CourseID = course.CourseID, Title = course.Title, Assigned = instructorCourses.Contains(course.CourseID) }); } ViewBag.Courses = viewModel; }

 

該代碼對Courses導航屬性進行了預先加載,而且調用了一個新的PopulateAssignedCourseData方法使用AssignedCourseData視圖模型類來爲複選框數組提供信息。

PopulateAssignedCourse方法中的代碼經過讀取全部Course實體並使用模型視圖類以加載列表。在每一個課程中,代碼檢查講師的Courses導航屬性中是否存在該課程。爲了建立一個高效的檢查一個課程是否指派給教師,已經分配的課程被放入一個HashSet集合。當課程已分配時,Assigned屬性爲True。視圖會使用該屬性來肯定哪些複選框應當顯示爲已選定。最後,該列表做爲ViewBag屬性被傳遞到視圖上。

下一步,添加用戶單擊保存時應當執行的代碼。調用一個新方法來更新Instructor實體的Courses導航屬性,使用下面的代碼替換EditPost方法,高亮部分是你進行的更改:

        [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.Entry(instructorToUpdate).State = EntityState.Modified;
                    db.SaveChanges();
                    return RedirectToAction("Index");
                }
                catch (RetryLimitExceededException)
                {
                    ModelState.AddModelError("", "沒法保存更改,請重試或聯繫管理員");
                }
            }
            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。

因爲視圖沒有課程實體的集合,因此模型綁定器不能自動更新Courses導航屬性。不一樣於使用模型綁定器來更新Course導航屬性,你將在UpdateInstructorCourses方法中進行更新。所以,您須要將Course屬性從模型綁定器中排除。這不須要更改任何代碼,由於你正在使用的白名單重載列表中沒有包含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);
                    }
                }

 

在Edit視圖中,在辦公室分配字段的div元素以後,保存按鈕以前插入一個Courses字段的複選框組。

        <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表格,其中包含三列。在每一列中顯示了課程的編號和標題以及一個複選框。全部的複選框都使用同一個name"selectedCourses",通知模型綁定器將它們做爲一個組來進行處理。每一個複選框的Value屬性被設定爲CourseID的值,當頁面提交時,模型綁定器將一個僅包含了已選擇複選框的CourseID值做爲數組傳遞給控制器。

複選框最初呈現時,已經分配給教師的課程會帶有checked特性,被設置爲選中狀態。

在你更改課程分配後,你會想要可以返回索引頁來驗證這些更改。所以,您須要將課程列添加到頁面的表格中。在這種狀況下你不須要使用ViewBag對象,由於你想要顯示的信息已經在Instructor實體的Courses導航屬性中並做爲模型傳遞給視圖了。

在Views\Instructor\Index.cshtml中,在辦公室標題後添加課程標題,以下圖所示:

<table class="table">
    <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(); instructor.OfficeAssignment = null;
            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方法不是由於有可能有課程被選擇,而是爲了提供一個空集合用於在視圖中循環。(不然會拋出一個空引用異常)

HttpPost的Create方法在將每一個選擇的課程添加到課程導航屬性及將新教師添加到數據庫前進行錯誤檢查。在模型有錯誤(例如,用戶輸入的無效日期)時,課程不會被添加。在頁面從新顯示一條錯誤信息時,所作的任何課程選擇都會被還原。

請注意,爲了能將課程添加到Courses導航屬性中,你必須初始化一個空集合:

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

 

做爲另外一種替代方法,你能夠在Course模型中修改屬性getter設置器來在它不存在時自動建立一個集合,以下面的代碼所示:

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

 

若是您使用上面的方法修改了模型代碼,您能夠再控制器中刪除初始化空集合的代碼。

在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>

記得在粘貼代碼後調整@:的縮進,跟以前你在Edit視圖中所作的同樣。

運行應用程序並嘗試建立一名教師。

處理事務

正如在基本CRUD功能教程中所解釋的那樣,實體框架默認會隱式地實現事務,在你須要更多的控制時,請參閱MSDN上的Working with Transactions

總結

如今你已經完成了本教程的所有相關數據。到目前爲止你都是經過同步IO來進行工做的,咱們會在下一節中介紹如何經過異步IO來更有效地使用服務器資源。

 

 

做者信息

  Tom Dykstra - Tom Dykstra是微軟Web平臺及工具團隊的高級程序員,做家。

相關文章
相關標籤/搜索