- ASP.NET MVCアプリケーションのEntity Frameworkデータモデルの作成
- ASP.NET MVCアプリケーションでEntity Frameworkを使用して基本的なCRUD機能を実装する
- ASP.NET MVCアプリケーションでEntity Frameworkを使用して並べ替え、フィルター処理、およびページ分割を行う
- ASP.NET MVCアプリケーション用の複雑なデータモデルの作成
- ASP.NET MVCアプリケーションの複雑なデータモデルの作成、パート2
この図は、作業の結果を示しています。
必要なデータのレイジー、イーガー、および明示的なロード
EFがエンティティのナビゲーションプロパティに必要なデータをロードするために使用するいくつかのメソッドがあります。
- 遅延 読み込み 。 エンティティへの最初の呼び出しでは、対応する関連データはロードされません。 ただし、ナビゲーションプロパティに初めてアクセスすると、関連付けられたデータが自動的にロードされます。 同時に、データベースに対して多くのクエリが実行されます。1つはエンティティ用で、もう1つはデータがダウンロードされるたびに実行されます。
- 熱心な ロード 。 エンティティにアクセスすると、データがロードされます。 通常、この後にすべてのデータを返す単一の結合要求が続きます。 Includeメソッドを使用して、イーガーロードの使用を指定できます。
- 明示的な ロード 。 このメソッドは、ユーザー自身がデータのロードを指定することを除いて、遅延ロードに似ています-これは、ナビゲーションプロパティにアクセスするときに自動的に行われません。 データは、エンティティの状態状態マネージャーを使用して手動でロードされ、コレクションのCollection.Loadメソッドと単一値プロパティのReference.Loadメソッドを呼び出します。 (この例では、管理者のナビゲーションプロパティを読み込む場合は、コレクション(x => x.Courses)を参照(x => x.Administrator)に置き換えます。)
データがすぐにロードされないため、遅延ロードと明示的ロードはまとめて遅延 ロードと呼ばれます。
一般に、各エンティティのデータが必要な場合は、通常、1つのリクエストが各エンティティの複数のリクエストよりも効率的であるため、イーガーロード方式が最高のパフォーマンスを提供します。 たとえば、各学部に10のコースがあると想像してください。 熱心なロードの例の後に、1つの結合要求が続きます。 遅延読み込みと明示的な読み込みの例の後には11が続きます。
一方、ナビゲーションプロパティがめったにアクセスされない場合、または少数のエンティティが使用される場合、遅延読み込みはより効率的になります-熱心な読み込みは必要以上のデータを読み込みます。 通常、明示的な読み込みは、遅延読み込みがオフになっている場合にのみ使用されます。 すべてのナビゲーションプロパティが必要ではないことが確実なときに、シリアル化中に遅延読み込みをオフにできる状況があるとします。 遅延読み込みが有効な場合、シリアル化はすべてのプロパティを参照するため、すべてのナビゲーションプロパティが自動的に読み込まれます。
データベースコンテキストクラスは、デフォルトで遅延ロードを使用します。 遅延読み込みをオフにする方法は2つあります。
- ナビゲーションプロパティを宣言するときは、仮想修飾子の指定をスキップします。
- すべてのナビゲーションプロパティについて、LazyLoadingEnabledをfalseとして指定します。
コース インデックスページの 作成
コースのエッセンスには、部門のエッセンス(このコースが属する部門のエッセンス)を含むナビゲーションプロパティがあります。 教員の名前を表示するには、対応するエンティティのNameプロパティを参照する必要があります。
タイプがCourseのコントローラーを作成します。
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>
以下の変更が行われました。
- タイトルがCoursesに変更されました。
- 行は左揃えです。
- CourseIDプロパティの値を表示するNumber見出しの下に列を追加しました。 (主キーは意味をなさないため、通常はページに含まれませんが、この場合、意味は意味があり、表示する必要があります。
- 最後の列の見出しはDepartmentに変更されます。
<td> @Html.DisplayFor(modelItem => item.Department.Name) </td>
コースを選択して、教員名のリストを表示します。
コースと生徒のリストを 含む インストラクター インデックス ページ を 作成する
インストラクターエンティティのコントローラーとビューを作成して、インストラクターインデックスページを表示します。
データのロードと表示は、次のシナリオに従って行われます。
- 教師リストには、対応するOfficeAssignmentエンティティデータが表示されます。 インストラクターとOfficeAssignmentエンティティは、1対0または1対1のエンティティです。 OfficeAssignmentエンティティは、積極的な読み込みを使用します。
- ユーザーが教師を選択すると、関連するコースエンティティが表示されます。 エンティティインストラクターとコースは多対多です。 コースエンティティとそれに関連する部門エンティティには、積極的な読み込みを使用します。 この場合、選択した教師のコースデータのみをダウンロードする必要があるため、遅延読み込みがより効果的です。 ただし、この例はイーガーロードの使用を示しています。
- ユーザーがコースを選択すると、登録エンティティのデータが表示されます。 コースおよび登録エンティティは1対多です。 Enrollmentエンティティとそれに関連するStudentエンティティに明示的な読み込みを追加します。 (遅延読み込みが有効になっている場合、明示的な読み込みの使用はオプションですが、明示的な読み込みがどのように機能するかを示します。)
インストラクターインデックスページには、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; }
インストラクター用のコントローラーとビューの作成
インストラクターのようなコントローラーを作成します。
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>
変更点:
- ページタイトルがインストラクターに変更されました。
- 行リンク列を左に移動しました。
- FullName列を削除しました。
- item.OfficeAssignmentがnullでない場合、item.OfficeAssignment.Locationを表示するOffice列を追加しました。 (1対0または1の関係があるため、OfficeAssignmentエンティティをエンティティに関連付けることはできません。)
<td> @if (item.OfficeAssignment != null) { @item.OfficeAssignment.Location } </td>
- 選択した教師のtrコンテナにclass = "selectedrow"を動的に追加するコードを追加しました。 このようにして、CSSクラスを使用して背景色を設定します。 (valign属性は、将来的にテーブルに複数行の列を追加するときに役立ちます)
string selectedRow = ""; if (item.InstructorID == ViewBag.InstructorID) { selectedRow = "selectedrow"; } <tr class="@selectedRow" valign="top">
- 新しいActionLink Selectが各行のリンクの前に追加されます。これにより、選択した教師のIDをIndexメソッドに渡すことができます。
ビュー \ インストラクター \ インデックスで 。 テーブルコンテナの後にある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メソッドに渡されます。
教師を選択すると、この教師のコースとコースが教えられている学部の一覧が表示されます。
選択した行の色が変わらない場合は、ページを更新してください。場合によっては、.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> }
このコードは、登録ビューモデルプロパティをロードして、選択した学生コースの学生のリストを表示します。
教師を選択してコースをクリックすると、生徒と成績が表示されます。
明示的な 読み込みの 追加
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 )の翻訳にご協力いただきありがとうございます。