- ASP.NET MVCアプリケーションのEntity Frameworkデータモデルの作成
- ASP.NET MVCアプリケーションでEntity Frameworkを使用して基本的なCRUD機能を実装する
- ASP.NET MVCアプリケーションでEntity Frameworkを使用して並べ替え、フィルター処理、およびページ分割を行う
- ASP.NET MVCアプリケーション用の複雑なデータモデルの作成
- ASP.NET MVCアプリケーションの複雑なデータモデルの作成、パート2
- ASP.NET MVCアプリケーションでEntity Frameworkからデータをロードする
結果は図に示されています。
コースの作成および編集ページの編集
コースの新しいエッセンスを作成するときは、既存の教員と接続する必要があります。 これを確実にするために、生成されたコードには、コントローラーメソッドと、教員を強調表示するドロップダウンリスト付きの作成および編集ビューが含まれています。 ドロップダウンリストは、外部キープロパティCourse.DepartmentIDを定義します。EFは、Departmentナビゲーションプロパティに対応するDepartmentエンティティをロードするために必要なすべてのものです。 生成されたコードを少し修正して使用し、エラーを処理し、ドロップダウンリストのアイテムを並べ替えます。
CourseController.csで 、EditおよびCreateメソッドのコードを置き換えます。
public ActionResult Create() { PopulateDepartmentsDropDownList(); return View(); } [HttpPost] public ActionResult Create(Course course) { try { if (ModelState.IsValid) { db.Courses.Add(course); db.SaveChanges(); return RedirectToAction("Index"); } } catch (DataException) { //Log the error (add a variable name after DataException) 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) { Course course = db.Courses.Find(id); PopulateDepartmentsDropDownList(course.DepartmentID); return View(course); } [HttpPost] public ActionResult Edit(Course course) { try { if (ModelState.IsValid) { db.Entry(course).State = EntityState.Modified; db.SaveChanges(); return RedirectToAction("Index"); } } catch (DataException) { //Log the error (add a variable name after DataException) ModelState.AddModelError("", "Unable to save changes. Try again, and if the problem persists, see your system administrator."); } PopulateDepartmentsDropDownList(course.DepartmentID); return View(course); } 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); }
PopulateDepartmentsDropDownListメソッドは、名前でソートされたすべての部門のリストをロードし、ドロップダウンリストのSelectListコレクションを作成し、ViewBagプロパティのビューにコレクションを渡します。 このメソッドは、呼び出し元がオプションでデフォルト項目を決定できるようにするパラメーターを受け入れます。
HttpGet createメソッドは、選択した項目を指定せずにPopulateDepartmentsDropDownListメソッドを呼び出します。これは、教員の新しいコースがまだ指定されていないためです。
public ActionResult Create() { PopulateDepartmentsDropDownList(); return View(); }
HttpGet Editメソッドは、編集可能なコースに関連付けられた教員IDに基づいて、選択されたアイテムを決定します。
public ActionResult Edit(int id) { Course course = db.Courses.Find(id); PopulateDepartmentsDropDownList(course.DepartmentID); return View(course); }
CreateおよびEditのHttpPostメソッドには、同様のコードが含まれています。
catch (DataException) { //Log the error (add a variable name after DataException) ModelState.AddModelError("", "Unable to save changes. Try again, and if the problem persists, see your system administrator."); } PopulateDepartmentsDropDownList(course.DepartmentID); return View(course);
このコードを使用すると、ページを更新してエラーメッセージを表示したときに、以前に選択した教員が選択されたままになるようにすることができます。
ビュー \ コース \ 作成で 。 cshtmlは、 タイトルフィールドの前に新しいフィールドを追加して、ユーザーがコース番号を入力します。 主キーのプロパティはビューで生成されないが、この場合、主キーには意味があるため、その値を入力する機会をユーザーに与える必要があると以前に説明しました。
<div class="editor-label"> @Html.LabelFor(model => model.CourseID) </div> <div class="editor-field"> @Html.EditorFor(model => model.CourseID) @Html.ValidationMessageFor(model => model.CourseID) </div>
Views \ Course \ Edit.cshtml 、 Views \ Course \ Delete.cshtml 、およびViews \ Course \ Details.cshtmlで、 タイトルフィールドの前に新しいフィールドを追加して、コース番号を表示します。 これは主キーであるため、表示する必要がありますが、編集は禁止する必要があります。
<div class="editor-label"> @Html.LabelFor(model => model.CourseID) </div> <div class="editor-field"> @Html.DisplayFor(model => model.CourseID) </div>
プロジェクトを実行し、 作成ページに移動して、新しいコースのデータを入力します。
作成をクリックします 。 コースインデックスページに、追加されたコースのリストが表示されます。 教員の名前はナビゲーションプロパティから取得されるため、エンティティ間の接続が正しく確立されていることが証明されます。
編集ページを開きます(コースインデックスページを開き、コースの[ 編集 ]をクリックします)。
データを変更し、[保存]をクリックします。 更新されたコースデータを含むコースインデックスページが表示されます。
インストラクター用の編集ページの追加
教師に関するレコードを編集するとき、彼のオフィスに関するレコードも更新できます。 インストラクターエンティティは、OfficeAssignmentに1対0または1対1で関連付けられます。つまり、次の状況を処理する必要があります。
- ユーザーがオフィスへのバインディングを削除した場合、OfficeAssignmentエンティティを削除する必要があります。
- ユーザーがオフィスプロパティに誤った値を入力した場合、新しいOfficeAssignmentエンティティを作成する必要があります。
- ユーザーがオフィスレコードの値を変更した場合、対応するOfficeAssignmentエンティティの値を変更する必要があります。
public ActionResult Edit(int id) { Instructor instructor = db.Instructors.Find(id); ViewBag.InstructorID = new SelectList(db.OfficeAssignments, "InstructorID", "Location", instructor.InstructorID); return View(instructor); }
生成されたコードは私たちには適していません-ドロップダウンリストのデータを初期化しますが、テキストフィールドが必要なので、このコードを次のように置き換えます
public ActionResult Edit(int id) { Instructor instructor = db.Instructors .Include(i => i.OfficeAssignment) .Include(i => i.Courses) .Where(i => i.InstructorID == id) .Single(); return View(instructor); }
このコードはViewBagを使用せず、関連するOfficeAssignmentおよびCourseエンティティの積極的な読み込みを定義します。 (コースは後で必要になります。)Findメソッドに積極的な読み込みを定義することはできないため、代わりにWhereメソッドとSingleメソッドを使用して教師を選択します。
HttpPost Editメソッドのコードを、オフィスエントリの編集を処理する次のコードに置き換えます。
[HttpPost] public ActionResult Edit(int id, FormCollection formCollection) { var instructorToUpdate = db.Instructors .Include(i => i.OfficeAssignment) .Include(i => i.Courses) .Where(i => i.InstructorID == id) .Single(); if (TryUpdateModel(instructorToUpdate, "", null, new string[] { "Courses" })) { try { if (String.IsNullOrWhiteSpace(instructorToUpdate.OfficeAssignment.Location)) { instructorToUpdate.OfficeAssignment = null; } db.Entry(instructorToUpdate).State = EntityState.Modified; db.SaveChanges(); return RedirectToAction("Index"); } catch (DataException) { //Log the error (add a variable name after DataException) ModelState.AddModelError("", "Unable to save changes. Try again, and if the problem persists, see your system administrator."); return View(); } } return View(instructorToUpdate); }
このコードは次の機能を実行します。
- OfficeAssignmentおよびCoursesナビゲーションプロパティの積極的な読み込みを使用して、データベースからInstructorエンティティを返します。 HttpGet Editメソッドについても同じことを行いました。
- コースナビゲーションプロパティを除くインストラクターエンティティを更新します。
If(TryUpdateModel(instructorToUpdate、 ""、null、new string [] {"Courses"}))
チェックに失敗した場合、TryUpdateModelはfalseを返し、実行はViewを返します。 - オフィスエントリが空の場合、Instructor.OfficeAssignmentプロパティはnullに設定されるため、OfficeAssignmentテーブルは削除されます。
if (String.IsNullOrWhiteSpace(instructorToUpdate.OfficeAssignment.Location)) { instructorToUpdate.OfficeAssignment = null; }
- 変更をデータベースに保存します。
<div class="editor-label"> @Html.LabelFor(model => model.OfficeAssignment.Location) </div> <div class="editor-field"> @Html.EditorFor(model => model.OfficeAssignment.Location) @Html.ValidationMessageFor(model => model.OfficeAssignment.Location) </div>
[ 講師 ]タブを選択し、教師の[ 編集 ]をクリックします。
オフィスの 場所の値を変更し、[ 保存 ]をクリックします 。
[インデックス]ページに新しいアドレスが表示されます 。 サーバー エクスプローラーで OfficeAssignmentテーブルを開くと、テーブルにエントリが表示されます 。
[編集]ページに戻り、[ オフィスの 場所 ]をクリアして、[ 保存 ]をクリックします 。 [インデックス]ページには空白のアドレスが表示され、 サーバー エクスプローラーにはレコードが削除されたことが表示されます。
[ 編集]ページで、[ オフィスの 場所 ]に新しい値を入力し、[ 保存 ]をクリックします 。 [ インデックス]ページに新しいアドレス値が表示され、 サーバー エクスプローラーに新しいエントリが表示されます。
講師の編集ページにコースの割り当てを追加する
教師はコースを無制限に教えることができます。 コースに割り当てる機能を追加して、インストラクター編集ページを更新します。
CourseとInstructorの関係は多対多として定義されているため、結合テーブルや外部キーへのアクセスはありません。 代わりに、Instructor.Coursesナビゲーションプロパティを操作する必要があります。
教師へのコースのバインドを変更する機能を提供するインターフェイスは、チェックボックスグループに囲まれています。
データをビューに転送してチェックボックスグループを生成するには、ビューモデルクラスを使用する必要があります。 ViewModelsフォルダーに次の内容のAssignedCourseData.csを作成します。
using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; 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) { Instructor instructor = db.Instructors .Include(i => i.OfficeAssignment) .Include(i => i.Courses) .Where(i => i.InstructorID == id) .Single(); 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; }
新しいメソッドのコードは、すべてのCourseエンティティをロードして、コースのリストを作成します。 各コースについて、インストラクターのコースナビゲーションプロパティに存在のチェックがあります。 コースが教師に関連付けられているかどうかを効果的に調べるために、教師関連のコースがHashSetコレクションに配置されます。 割り当てられたコースの割り当てられたコースプロパティはtrueに設定されます。 ビューはこのプロパティを使用して、チェックボックスをオンにするかどうかを決定します。 その後、リストはViewBagプロパティでビューに渡されます。
[保存]ボタンハンドラコードを追加します。HttpPostEditメソッドコードを、InstructorエンティティのCoursesナビゲーションプロパティを更新する新しいメソッドを呼び出すコードに置き換えます。
[HttpPost] public ActionResult Edit(int id, FormCollection formCollection, string[] selectedCourses) { var instructorToUpdate = db.Instructors .Include(i => i.OfficeAssignment) .Include(i => i.Courses) .Where(i => i.InstructorID == id) .Single(); if (TryUpdateModel(instructorToUpdate, "", null, new string[] { "Courses" })) { try { UpdateModel(instructorToUpdate, "", null, new string[] { "Courses" }); if (String.IsNullOrWhiteSpace(instructorToUpdate.OfficeAssignment.Location)) { instructorToUpdate.OfficeAssignment = null; } UpdateInstructorCourses(selectedCourses, instructorToUpdate); db.Entry(instructorToUpdate).State = EntityState.Modified; db.SaveChanges(); return RedirectToAction("Index"); } catch (DataException) { //Log the error (add a variable name after DataException) 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); } } } }
チェックボックスが選択されていない場合、UpdateInstructorCoursesのコードは、空のコレクションでCoursesナビゲーションプロパティを初期化します。
if (selectedCourses == null) { instructorToUpdate.Courses = new List(); return; }
コードはデータベース内のすべてのコースレコードを反復処理し、コースのチェックボックスがオンになっているが、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で 、OfficeAssignmentのdivコンテナーの直後にチェックボックスグループを持つCoursesフィールドを追加します。
<div class="editor-field"> <table style="width: 100%"> <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>
このコードは、3つの列のHTMLテーブルを作成します。各列には、コース番号と名前の見出しが付いたチェックボックスが含まれています。 すべてのチェックボックスには同じ名前(「selectedCourses」)があり、同じグループに属することを象徴しています。 各チェックボックスの値属性はCourseIDに設定されています。 ページがデータを送信すると、選択されたチェックボックスとCourseID値で構成される配列がコントローラーに渡されます。
チェックボックスが生成されると、すでに教師に割り当てられているものにチェック属性があります。
教師へのコースバインドを変更した後、インデックスページに戻るときに変更を確認する必要があります。 これを行うには、このページのテーブルに列を追加します。 これを行うには、ViewBagオブジェクトを使用する必要はありません。表示する必要がある情報は、モデルとしてビューに渡されるInstructorエンティティのCoursesナビゲーションプロパティに既にあるためです。
Views \ Instructor \ Index.cshtmlで 、ヘッダーセル<th> Courses </ th>を<th> Office </ th>の直後に追加します。
<tr> <th></th> <th>Last Name</th> <th>First Name</th> <th>Hire Date</th> <th>Office</th> <th>Courses</th> </tr>
その後、オフィスの住所を持つセルの直後に新しいセルを追加します。
<td> @{ foreach (var course in item.Courses) { @course.CourseID @: @course.Title <br /> } } </td>
プロジェクトを実行し、 インストラクター インデックスページに移動します。
教師の[ 編集]をクリックします。
コースのバインドを変更し、[保存]をクリックします。 変更は[インデックス]ページに表示されます。
納入品を使用した作業のレッスンの紹介が完了しました。 単純なCRUD操作を完了しましたが、同時実行性の問題には対処しませんでした。 次のレッスンでは、並列処理のトピック、それを扱う問題に専念します。
謝辞
Alexander Belotserkovskyの翻訳にご協力いただきありがとうございます。