トレーニングコース。 ASP.NET MVCアプリケーションでEntity Frameworkからデータをロードする

これは、Entity FrameworkおよびASP.NET MVC 3開発記事シリーズの拡張であり、最初の章は次のリンクにあります。

前回のレッスンでは、学校データモデルの作業を完了しました。 このレッスンでは、Entity Frameworkによって読み込まれた必要なデータをナビゲーションプロパティに読み込んで表示します。



この図は、作業の結果を示しています。



clip_image001



clip_image002



必要なデータのレイジー、イーガー、および明示的なロード



EFがエンティティのナビゲーションプロパティに必要なデータをロードするために使用するいくつかのメソッドがあります。

clip_image003

clip_image004

clip_image005



データがすぐにロードされないため、遅延ロードと明示的ロードはまとめて遅延 ロードと呼ばます。



一般に、各エンティティのデータが必要な場合は、通常、1つのリクエストが各エンティティの複数のリクエストよりも効率的であるため、イーガーロード方式が最高のパフォーマンスを提供します。 たとえば、各学部に10のコースがあると想像してください。 熱心なロードの例の後に、1つの結合要求が続きます。 遅延読み込みと明示的な読み込みの例の後には11が続きます。



一方、ナビゲーションプロパティがめったにアクセスされない場合、または少数のエンティティが使用される場合、遅延読み込みはより効率的になります-熱心な読み込みは必要以上のデータを読み込みます。 通常、明示的な読み込みは、遅延読み込みがオフになっている場合にのみ使用されます。 すべてのナビゲーションプロパティが必要ではないことが確実なときに、シリアル化中に遅延読み込みをオフにできる状況があるとします。 遅延読み込みが有効な場合、シリアル化はすべてのプロパティを参照するため、すべてのナビゲーションプロパティが自動的に読み込まれます。



データベースコンテキストクラスは、デフォルトで遅延ロードを使用します。 遅延読み込みをオフにする方法は2つあります。

遅延読み込みは、パフォーマンスの問題を引き起こすコードを隠す場合があります。 たとえば、積極的または明示的な読み込みを使用せず、ループ内で多数のエンティティといくつかのナビゲーションプロパティを使用するコードは、データベース呼び出しが多数あるため非常に効率が悪い場合がありますが、遅延読み込みを使用すればすべてうまくいきます。 遅延読み込みを一時的に無効にすることは、遅延読み込みの使用に焦点を当てたコードを見つける1つの方法です。 このナビゲーションプロパティではNullになり、エラーがトリガーされます。



コース インデックスページの 作成



コースのエッセンスには、部門のエッセンス(このコースが属する部門のエッセンス)を含むナビゲーションプロパティがあります。 教員の名前を表示するには、対応するエンティティのNameプロパティを参照する必要があります。



タイプがCourseのコントローラーを作成します。



clip_image006



Controllers \ CourseController.csで 、Indexメソッドに注意してください。



public ViewResult Index() { var courses = db.Courses.Include(c => c.Department); return View(courses.ToList()); }
      
      





自動スキャフォールディングは、Includeメソッドを使用して、部門ナビゲーションプロパティのイーガーロードの使用を決定します。



Views \ Course \ Index.cshtmlで 、コードを次のように置き換えます。



 @model IEnumerable<ContosoUniversity.Models.Course> @{ ViewBag.Title = "Courses"; } <h2>Courses</h2> <p> @Html.ActionLink("Create New", "Create") </p> <table> <tr> <th></th> <th>Number</th> <th>Title</th> <th>Credits</th> <th>Department</th> </tr> @foreach (var item in Model) { <tr> <td> @Html.ActionLink("Edit", "Edit", new { id=item.CourseID }) | @Html.ActionLink("Details", "Details", new { id=item.CourseID }) | @Html.ActionLink("Delete", "Delete", new { id=item.CourseID }) </td> <td> @Html.DisplayFor(modelItem => item.CourseID) </td> <td> @Html.DisplayFor(modelItem => item.Title) </td> <td> @Html.DisplayFor(modelItem => item.Credits) </td> <td> @Html.DisplayFor(modelItem => item.Department.Name) </td> </tr> } </table>
      
      





以下の変更が行われました。



最後の列には、DepartmentナビゲーションプロパティのDepartmentエンティティのNameプロパティの値が表示されることに注意してください。



 <td> @Html.DisplayFor(modelItem => item.Department.Name) </td>
      
      





コースを選択して、教員名のリストを表示します。



clip_image001[1]



コースと生徒のリストを 含む インストラクター インデックス ページ 作成する



インストラクターエンティティのコントローラーとビューを作成して、インストラクターインデックスページを表示します。



clip_image002[1]



データのロードと表示は、次のシナリオに従って行われます。

インストラクター インデックス ビューのプレゼンテーションモデルの作成



インストラクターインデックスページには、3つの異なるテーブルが表示されます。 これを行うには、3つのプロパティを含むプレゼンテーションモデルを作成します。各プロパティには、各テーブルのデータが含まれます。



ViewModelsフォルダーでInstructorIndexData.csを作成します。



 using System; using System.Collections.Generic; using ContosoUniversity.Models; namespace ContosoUniversity.ViewModels { public class InstructorIndexData { public IEnumerable<Instructor> Instructors { get; set; } public IEnumerable<Course> Courses { get; set; } public IEnumerable<Enrollment> Enrollments { get; set; } } }
      
      





選択した列のスタイル



選択した列には、個別の背景色が必要です。 これを行うには、次のコードをContent \ Siteに追加します css



 /* MISC ----------------------------------------------------------*/ .selectedrow { background-color: #EEEEEE; }
      
      





インストラクター用のコントローラーとビューの作成



インストラクターのようなコントローラーを作成します。



clip_image007



Controllers \ InstructorController.csで 、ViewModelsを使用して追加します



ContosoUniversity.ViewModelsを使用します。



Indexメソッドで生成されたコードは、OfficeAssignmentナビゲーションプロパティに対してのみイーガーロードの使用を決定します。



 public ViewResult Index() { var instructors = db.Instructors.Include(i => i.OfficeAssignment); return View(instructors.ToList()); }
      
      





Indexメソッドを次のコードに置き換えます。これにより、追加データがロードされ、ビューモデルに配置されます。



 public ActionResult Index(Int32? id, Int32? courseID) { var viewModel = new InstructorIndexData(); viewModel.Instructors = db.Instructors .Include(i => i.OfficeAssignment) .Include(i => i.Courses.Select(c => c.Department)) .OrderBy(i => i.LastName); if (id != null) { ViewBag.InstructorID = id.Value; viewModel.Courses = viewModel.Instructors.Where(i => i.InstructorID == id.Value).Single().Courses; } if (courseID != null) { ViewBag.CourseID = courseID.Value; viewModel.Enrollments = viewModel.Courses.Where(x => x.CourseID == courseID).Single().Enrollments; } return View(viewModel); }
      
      





このメソッドは、オプションの文字列パラメーター(選択した教師とコースのID値)を受け入れ、必要なデータをプレゼンテーションに渡します。 IDは、ページの選択リンクから取得されます。



コードは、プレゼンテーションモデルのインスタンスを作成し、教師のリストをそこに渡すことから始まります。



 var viewModel = new InstructorIndexData(); viewModel.Instructors = db.Instructors .Include(i => i.OfficeAssignment); .Include(i => i.Courses.Select(c => c.Department)) .OrderBy(i => i.LastName);
      
      





Instructor.OfficeAssignmentおよびInstructor.Coursesナビゲーションプロパティの積極的な読み込みを定義します。 関連するエンティティの場合、インクルードメソッドのSelectメソッドを使用して、Course.Departmentナビゲーションプロパティのコースの積極的な読み込みが定義されます。 結果は姓でソートされます。



教師が選択されている場合、この教師はデータモデルの教師のリストから取得され、その後、その教師に対応するコースナビゲーションプロパティのコースエンティティによってCoursesプロパティが初期化されます。



 if (id != null) { ViewBag.InstructorID = id.Value; viewModel.Courses = viewModel.Instructors.Where(i => i.InstructorID == id.Value).Single().Courses; }
      
      





Whereメソッドはコレクションを返しますが、この場合、メソッドに渡される条件は、1つのInstructorエンティティのみが返されることを示しています。 Singleメソッドは、コレクションを1つのInstructorエンティティに変換します。これにより、このエンティティに対応するCoursesプロパティにアクセスできます。



コレクションが1つの要素で構成されることがわかっている場合は、コレクションでSingleメソッドが使用されます。 このメソッドは、コレクションが空であるか、複数の要素で構成されている場合に例外をスローします。 ただし、この場合、例外がスローされます(参照がnullのCourseプロパティのため)。 Whereを個別に呼び出す代わりに、Singleを呼び出す場合、条件自体を渡すことができます。



.Single(i => i.InstructorID == id.Value)



代わりに:



.Where(I => i.InstructorID == id.Value).Single()



さらに、コースが選択されている場合、このコースはモデル内のコースのリストから抽出されます。 次に、EnrollmentsモデルプロパティがEnrollmentsナビゲーションプロパティエンティティによって初期化されます。



 if (courseID != null) { ViewBag.CourseID = courseID.Value; viewModel.Enrollments = viewModel.Courses.Where(x => x.CourseID == courseID).Single().Enrollments; }
      
      





そして最後に、モデルに戻ります。



Return View(viewModel);



インストラクターインデックス ビューの 編集



Views \ Instructor \ Index.cshtmlで 、コードを次のように置き換えます。



 @model ContosoUniversity.ViewModels.InstructorIndexData @{ ViewBag.Title = "Instructors"; } <h2>Instructors</h2> <p> @Html.ActionLink("Create New", "Create") </p> <table> <tr> <th></th> <th>Last Name</th> <th>First Name</th> <th>Hire Date</th> <th>Office</th> </tr> @foreach (var item in Model.Instructors) { string selectedRow = ""; if (item.InstructorID == ViewBag.InstructorID) { selectedRow = "selectedrow"; } <tr class="@selectedRow" valign="top"> <td> @Html.ActionLink("Select", "Index", new { id = item.InstructorID }) | @Html.ActionLink("Edit", "Edit", new { id = item.InstructorID }) | @Html.ActionLink("Details", "Details", new { id = item.InstructorID }) | @Html.ActionLink("Delete", "Delete", new { id = item.InstructorID }) </td> <td> @item.LastName </td> <td> @item.FirstMidName </td> <td> @String.Format("{0:d}", item.HireDate) </td> <td> @if (item.OfficeAssignment != null) { @item.OfficeAssignment.Location } </td> </tr> } </table>
      
      





変更点:



 <td> @if (item.OfficeAssignment != null) { @item.OfficeAssignment.Location } </td>
      
      







 string selectedRow = ""; if (item.InstructorID == ViewBag.InstructorID) { selectedRow = "selectedrow"; } <tr class="@selectedRow" valign="top">
      
      





プロジェクトを実行して、教師のリストを表示します。 OfficeAssignmentエンティティに関連付けられたLocationプロパティと、OfficeAssignment関連のエンティティがない場合は空白のセルがページに表示されます。



clip_image008



ビュー \ インストラクター \ インデックスで テーブルコンテナの後にあるcshtmlは、選択した教師のコースのリストを示すコードを追加します。



 @if (Model.Courses != null) { <h3>Courses Taught by Selected Instructor</h3> <table> <tr> <th></th> <th>ID</th> <th>Title</th> <th>Department</th> </tr> @foreach (var item in Model.Courses) { string selectedRow = ""; if (item.CourseID == ViewBag.CourseID) { selectedRow = "selectedrow"; } <tr class="@selectedRow"> <td> @Html.ActionLink("Select", "Index", new { courseID = item.CourseID }) </td> <td> @item.CourseID </td> <td> @item.Title </td> <td> @item.Department.Name </td> </tr> } </table> }
      
      





このコードは、コースビューモデルプロパティをロードしてコースのリストを表示し、選択リンクを表示します。選択リンクを介して、選択したコースのIDがIndexメソッドに渡されます。



教師を選択すると、この教師のコースとコースが教えられている学部の一覧が表示されます。



clip_image009



選択した行の色が変わらない場合は、ページを更新してください。場合によっては、.cssファイルをダウンロードする必要があります。



追加したコードの後に​​、選択したコースの学生のリストを表示するコードを追加します。



 @if (Model.Enrollments != null) { <h3> Students Enrolled in Selected Course</h3> <table> <tr> <th>Name</th> <th>Grade</th> </tr> @foreach (var item in Model.Enrollments) { <tr> <td> @item.Student.FullName </td> <td> @item.Grade </td> </tr> } </table> }
      
      





このコードは、登録ビューモデルプロパティをロードして、選択した学生コースの学生のリストを表示します。



教師を選択してコースをクリックすると、生徒と成績が表示されます。



clip_image002[2]



明示的な 読み込みの 追加



InstructorControllerで csおよびIndexメソッドが選択したコースの学生のリストをロードする方法に注目してください。



 if (courseID != null) { ViewBag.CourseID = courseID.Value; viewModel.Enrollments = viewModel.Courses.Where(x => x.CourseID == courseID).Single().Enrollments; }
      
      





教師のリストを読み込むときに、コースナビゲーションプロパティと各学科コースのプロパティの積極的な読み込みを決定し、コースコレクションをプレゼンテーションモデルに送信し、このコレクション内のエンティティの1つから登録ナビゲーションプロパティを読み込みます。 Course.Enrollmentsナビゲーションプロパティの積極的な読み込みを定義しなかったため、遅延読み込みの結果、このプロパティのデータがページに表示されます。



遅延読み込みを無効にした場合、このコースに参加している生徒数に関係なく、Enrollmentsプロパティはnullになります。 この場合、Enrollmentsプロパティを初期化するには、そのプロパティのイーガーロードまたは明示的なロードを定義する必要があります。 明示的な読み込みを決定するには、Indexメソッドのコードを次のように置き換えます。



 public ActionResult Index(Int32? id, Int32? courseID) { var viewModel = new InstructorIndexData(); viewModel.Instructors = db.Instructors .Include(i => i.OfficeAssignment) .Include(i => i.Courses.Select(c => c.Department)) .OrderBy(i => i.LastName); if (id != null) { ViewBag.InstructorID = id.Value; viewModel.Courses = viewModel.Instructors.Where(i => i.InstructorID == id.Value).Single().Courses; } if (courseID != null) { ViewBag.CourseID = courseID.Value; var selectedCourse = viewModel.Courses.Where(x => x.CourseID == courseID).Single(); db.Entry(selectedCourse).Collection(x => x.Enrollments).Load(); foreach (Enrollment enrollment in selectedCourse.Enrollments) { db.Entry(enrollment).Reference(x => x.Student).Load(); } viewModel.Enrollments = viewModel.Courses.Where(x => x.CourseID == courseID).Single().Enrollments; } return View(viewModel); }
      
      





選択したコースエンティティを読み込んだ後、新しいコードは登録プロパティを明示的に読み込みます。



db.Entry(selectedCourse).Collection(x => x.Enrollments).Load();



次に、生徒エンティティが明示的にロードされます。



db.Entry(登録).Reference(x => x.Student).Load();



Collectionメソッドを使用してコレクションプロパティを初期化しますが、1要素プロパティの場合はReferenceメソッドを使用します。 これで、インストラクターインデックスページを開くことができます。外部からは何も変更されていませんが、データのロードの原則は変更されています。



そのため、ナビゲーションプロパティにデータを読み込む3つの方法すべてを使用しました。 次のレッスンでは、関連データを更新する方法を学習します。



謝辞



Alexander Belotserkovsky( ahriman )の翻訳にご協力いただきありがとうございます。



All Articles