ASP.NET MVC アプリケーションでの Entity Framework での関連データの読み取り (5/10)
著者: Tom Dykstra
Contoso University のサンプル Web アプリケーションでは、Entity Framework 5 Code First と Visual Studio 2012 を使用して ASP.NET MVC 4 アプリケーションを作成する方法を示します。 チュートリアル シリーズについては、シリーズの最初のチュートリアルを参照してください。
Note
解決できない問題が発生した場合は、完了した章をダウンロードして、問題を再現してみてください。 通常、完成したコードと自分のコードを比較することで、問題の解決策を見つけることができます。 一般的なエラーとその解決方法については、「エラーと回避策」をご覧ください。
前のチュートリアルでは、School データ モデルを作成しました。 このチュートリアルでは、関連データ (Entity Framework がナビゲーション プロパティに読み込むデータ) の読み取りと表示を行います。
以下の図は、使用するページを示しています。
関連データの遅延読み込み、一括読み込み、明示的読み込み
Entity Framework がエンティティのナビゲーション プロパティに関連データを読み込むには、次の複数の方法があります。
遅延読み込み。 エンティティが最初に読み込まれるときに、関連データは取得されません。 ただし、ナビゲーション プロパティに初めてアクセスしようとすると、そのナビゲーション プロパティに必要なデータが自動的に取得されます。 これにより、エンティティ自体に対して 1 つと、エンティティの関連データを取得するたびに 1 つのように、複数のクエリがデータベースに送信されます。
一括読み込み。 エンティティが読み取られるときに、関連データがエンティティと共に取得されます。 これは通常、必要なすべてのデータを取得する 1 つの結合クエリになります。 一括読み込みは、
Include
メソッドを使用して指定します。明示的読み込み。 これは遅延読み込みと似ていますが、関連データをコードで明示的に取得する点が異なります。コードの取得は、ナビゲーション プロパティにアクセスしても自動的には発生しません。 関連データを手動で読み込むには、エンティティのオブジェクト状態マネージャー エントリを取得し、コレクションの
Collection.Load
メソッドを呼び出すか、単一のエンティティを保持するプロパティのReference.Load
メソッドを呼び出します。 (次の例で、Administrator ナビゲーション プロパティを読み込む場合は、Collection(x => x.Courses)
をReference(x => x.Administrator)
に置き換えます)。
プロパティ値はすぐには取得されないため、遅延読み込み (lazy loading) と明示的読み込み (explicit loading) はどちらも遅延読み込み (deferred loading) と呼ばれます。
一般に、取得したすべてのエンティティの関連データが必要な場合は、データベースに送信された 1 つのクエリの方が、取得した各エンティティに対する分離したクエリよりも効率的なため、一括読み込みを使用すると、より最適なパフォーマンスが得られます。 たとえば、上記の例で、各部門に 10 個の関連コースがあるとします。 一括読み込みの例では、1 つの (結合) クエリと 1 回のデータベースとのラウンド トリップだけになります。 遅延読み込みと明示的読み込みの例の両方で、11 個のクエリと 11 回のラウンド トリップがデータベースに行われます。 データベースとの余分なラウンド トリップは、特に待ち時間が長いときのパフォーマンスに悪影響をもたらします。
その一方で、一部のシナリオでは、遅延読み込みがより効率的です。 一括読み込みでは、非常に複雑な結合が生成される可能性があり、SQL Server では効率的に処理できません。 または、処理しているエンティティのセットのサブセットのためだけにエンティティのナビゲーション プロパティにアクセスする必要がある場合、一括読み込みでは、必要以上にデータを取得するため、遅延読み込みの方が適切に実行される可能性があります。 パフォーマンスが重要な場合、最適な選択を行うために、両方の方法でパフォーマンスをテストすることをお勧めします。
通常は、遅延読み込みをオフにした場合にのみ、明示的読み込みを使用します。 遅延読み込みをオフにする必要があるシナリオの 1 つとして、シリアル化の実行中があります。 遅延読み込みとシリアル化はうまく組み合わせることができません。注意しないと、遅延読み込みが有効になっているときに意図したよりも大幅に多くのデータをクエリする結果になる可能性があります。 シリアル化は通常、ある型のインスタンスの各プロパティにアクセスすることによって機能します。 プロパティ アクセスによって遅延読み込みがトリガーされ、それらの遅延読み込みされたエンティティがシリアル化されます。 その後、シリアル化プロセスは遅延読み込みされたエンティティの各プロパティにアクセスするため、潜在的に遅延読み込みとシリアル化がさらに促進される可能性があります。 このような連鎖反応を防ぐには、エンティティをシリアル化する前に遅延読み込みをオフにします。
データベース コンテキスト クラスは、既定で遅延読み込みを実行します。 遅延読み込みを無効にするには、次の 2 つの方法があります。
特定のナビゲーション プロパティの場合は、プロパティを宣言するときに
virtual
キーワードを省略します。すべてのナビゲーション プロパティの場合は、
LazyLoadingEnabled
をfalse
に設定します。 たとえば、コンテキスト クラスのコンストラクターに次のコードを配置できます。this.Configuration.LazyLoadingEnabled = false;
遅延読み込みでは、パフォーマンスの問題を引き起こすコードをマスクできます。 たとえば、一括読み込みまたは明示的読み込みを指定せず、大量のエンティティを処理し、各イテレーションで複数のナビゲーション プロパティを使用するコードは、非常に非効率的な場合があります (データベースへのラウンド トリップが多いため)。 オンプレミスの SQL Server を使用した開発で優れたパフォーマンスを発揮するアプリケーションでは、待機時間の増加と遅延読み込みが原因で、Azure SQL Database に移動したときにパフォーマンスの問題が発生する可能性があります。 現実的なテスト負荷でデータベース クエリをプロファイリングすると、遅延読み込みが適切かどうかを判断するのに役立ちます。 詳細については、Entity Framework 戦略の解明: 関連データの読み込みの記事と、Entity Framework を使用した SQL Azure へのネットワーク待機時間の短縮の記事を参照してください。
部署名を表示する Courses インデックス ページを作成する
Course
エンティティには、コースが割り当てられている部門の Department
エンティティを含む、ナビゲーション プロパティが含まれます。 コースのリストに割り当てられた部門の名前を表示するには、Course.Department
ナビゲーション プロパティにある Department
エンティティから Name
プロパティを取得する必要があります。
次の図に示すように、前に Student
コントローラーに対して行ったのと同じオプションを使用して、Course
エンティティ型の CourseController
という名前を付けたコントローラーを作成します (ただし、イメージとは異なり、コンテキスト クラスは Models 名前空間ではなく DAL 名前空間にあります)。
Controllers\CourseController.cs を開き、Index
メソッドを確認します。
public ViewResult Index()
{
var courses = db.Courses.Include(c => c.Department);
return View(courses.ToList());
}
自動スキャフォールディングでは、Include
メソッドを使って、Department
ナビゲーション プロパティに一括読み込みを指定しています。
Views/Courses/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>
スキャフォールディング コードに、次の変更を行いました。
- 見出しが Index から Courses に変更されました。
- 行リンクを左に移動しました。
- 見出し Number の下に
CourseID
プロパティ値を示す列を追加しました。 (既定では、主キーは、通常、エンド ユーザーにとって意味がないため、スキャフォールディングされません。ただし、このケースでは、主キーは意味があり、表示する必要があります)。 - 最後の列見出しを DepartmentID (
Department
エンティティへの外部キーの名前) から Department に変更しました。
最後の列のスキャフォールディングされたコードには、Department
ナビゲーション プロパティに読み込まれる Department
エンティティの Name
プロパティが表示されます。
<td>
@Html.DisplayFor(modelItem => item.Department.Name)
</td>
ページを実行して (Contoso University ホーム ページの [Courses] タブを選択)、部門名の一覧を表示します。
コースと登録を示す Instructors インデックス ページを作成する
このセクションでは、Instructors インデックス ページを表示するために、Instructor
エンティティのコントローラーとビューを作成します。
このページは、次の方法で関連データを読み取って表示します。
- インストラクターのリストには、
OfficeAssignment
エンティティからの関連データが表示されます。Instructor
エンティティとOfficeAssignment
エンティティは、一対ゼロまたは一対一のリレーションシップです。OfficeAssignment
エンティティに一括読み込みを使用します。 前述のように、通常、一括読み込みは、主テーブルで取得したすべての行の関連データが必要なときにより効率的です。 このケースでは、割り当てられたすべてのインストラクターのオフィスの割り当てを表示する必要があります。 - ユーザーがインストラクターを選択すると、関連する
Course
エンティティが表示されます。Instructor
エンティティとCourse
エンティティは多対多リレーションシップです。Course
エンティティとその関連Department
エンティティの一括読み込みを使用します。 このケースでは、選択したインストラクターのコースのみが必要なため、遅延読み込みの方が効率的な可能性があります。 ただし、この例では、ナビゲーション プロパティにあるエンティティ内のナビゲーション プロパティに一括読み込みを使用する方法を示します。 - ユーザーがコースを選択すると、
Enrollments
エンティティ セットからの関連データが表示されます。Course
エンティティとEnrollment
エンティティは一対多リレーションシップです。Enrollment
エンティティとその関連Student
エンティティの明示的読み込みを追加します。 (遅延読み込みが有効になっているため、明示的読み込みは必要ありませんが、ここでは明示的読み込みを行う方法を示しています)。
Instructor インデックス ビューのビュー モデルを作成する
Instructor インデックス ページには、3 つの異なるテーブルが表示されます。 そのため、テーブルの 1 つにデータを保持するごとに、3 つのプロパティを含むビュー モデルを作成します。
ViewModels フォルダー内に InstructorIndexData.cs を作成し、既存のコードを次のコードで置き換えます。
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; }
}
}
選択した行のスタイルの追加
選択した行をマークするには、別の背景色が必要です。 この UI のスタイルを指定するには、次に示すように、Content\Site.css のセクション /* info and errors */
に次の強調表示されたコードを追加します。
/* info and errors */
.selectedrow
{
background-color: #a4d4e6;
}
.message-info {
border: 1px solid;
clear: both;
padding: 10px 20px;
}
Instructor コントローラーとビューの作成
次の図に示すように、InstructorController
コントローラーを作成します。
Controllers\InstructorController.cs を開き、ViewModels
名前空間の using
ステートメントを追加します。
using ContosoUniversity.ViewModels;
Index
メソッドのスキャフォールディングされたコードは、OfficeAssignment
ナビゲーション プロパティに対してのみ一括読み込みを指定します。
public ViewResult Index()
{
var instructors = db.Instructors.Include(i => i.OfficeAssignment);
return View(instructors.ToList());
}
Index
メソッドを次のコードに置き換えて、追加の関連データを読み込み、ビュー モデルに配置します。
public ActionResult Index(int? id, int? 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 値を提供するクエリ文字列パラメーター (courseID
) を受け取り、必要なすべてのデータをビューに渡します。 パラメーターは、ページの Select ハイパーリンクによって指定されます。
ヒント
ルート データ
ルート データは、ルーティング テーブルで指定された URL セグメントでモデル バインダーが検出したデータです。 たとえば、既定のルートでは、controller
、action
、id
のセグメントが指定されます。
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
次の URL では、既定のルートは Instructor
を controller
として、Index
を action
として、1 を id
としてマッピングします。これらは、ルート データ値です。
http://localhost:1230/Instructor/Index/1?courseID=2021
"?courseID=2021" はクエリ文字列値です。 モデル バインダーは、id
をクエリ文字列値として渡す場合にも機能します。
http://localhost:1230/Instructor/Index?id=1&CourseID=2021
URL は Razor ビューの ActionLink
ステートメントによって作成されます。 次のコードでは、id
パラメーターが既定のルートと一致するため、id
がルート データに追加されます。
@Html.ActionLink("Select", "Index", new { id = item.PersonID })
次のコードでは、courseID
は既定ルートのパラメーターと一致しないため、クエリ文字列として追加されます。
@Html.ActionLink("Select", "Index", new { courseID = item.CourseID })
このコードは、ビュー モデルのインスタンスを作成し、インストラクターのリストに配置することから始めます。 コードでは、Instructor.OfficeAssignment
と Instructor.Courses
ナビゲーション プロパティに一括読み込みを指定します。
var viewModel = new InstructorIndexData();
viewModel.Instructors = db.Instructors
.Include(i => i.OfficeAssignment)
.Include(i => i.Courses.Select(c => c.Department))
.OrderBy(i => i.LastName);
2 番目の Include
メソッドは Courses を読み込み、読み込まれる各コースで Course.Department
ナビゲーション プロパティの一括読み込みを行います。
.Include(i => i.Courses.Select(c => c.Department))
前述のように、一括読み込みは必須ではありませんが、パフォーマンスを向上させるために行われます。 ビューには常に OfficeAssignment
エンティティが必要なため、同じクエリでフェッチする方が効率的です。 インストラクターが Web ページで選択されたときに、Course
エンティティが必要なため、一括読み込みが遅延読み込みよりも適しているのは、ページがコースが選択された状態で表示されることが、選択されない状態よりも多い場合のみです。
インストラクター ID が選択されている場合、選択したインストラクターはビュー モデルのインストラクターの一覧から取得されます。 次に、ビュー モデルの Courses
プロパティが Course
エンティティと共にそのインストラクターの Courses
ナビゲーション プロパティから読み込まれます。
if (id != null)
{
ViewBag.InstructorID = id.Value;
viewModel.Courses = viewModel.Instructors.Where(i => i.InstructorID == id.Value).Single().Courses;
}
Where
メソッドはコレクションを返しますが、このケースでは、そのメソッドに渡された条件は、返されている Instructor
エンティティの 1 つのみになります。 Single
メソッドを実行すると、コレクションが、エンティティの Courses
プロパティへのアクセス権を付与する 1 つの Instructor
エンティティに変換されます。
コレクションに項目が 1 つのみであることがわかっている場合、コレクションで Single メソッドを使用します。 渡されるコレクションが空になる場合、または複数の項目がある場合、Single
メソッドから例外がスローされます。 代わりに、コレクションが空の場合に既定値 (この場合は null
) を返す SingleOrDefault を使用します。 ただし、この場合は引き続き例外となり (null
参照で Courses
プロパティを見つけようとして)、例外メッセージでは問題の原因があまり明確に示されません。 Single
メソッドを呼び出す場合、個別に Where
メソッドを呼び出す代わりに、Where
条件で渡すこともできます。
.Single(i => i.InstructorID == id.Value)
これは次のコードの代わりに使用します。
.Where(I => i.InstructorID == id.Value).Single()
次に、コースが選択された場合、選択したコースはビュー モデルのコースのリストから取得されます。 次に、ビュー モデルの Enrollments
プロパティが Enrollment
エンティティと共にそのコースの Enrollments
ナビゲーション プロパティから読み込まれます。
if (courseID != null)
{
ViewBag.CourseID = courseID.Value;
viewModel.Enrollments = viewModel.Courses.Where(
x => x.CourseID == courseID).Single().Enrollments;
}
Instructor インデックス ビューの変更
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>
@Html.DisplayFor(modelItem => item.HireDate)
</td>
<td>
@if (item.OfficeAssignment != null)
{
@item.OfficeAssignment.Location
}
</td>
</tr>
}
</table>
既存のコードに次の変更を行いました。
モデル クラスが
InstructorIndexData
に変更されました。Index のページ タイトルが Instructors に変更されました。
行リンク列を左に移動しました。
FullName 列を削除しました。
item.OfficeAssignment
が null ではない場合にのみitem.OfficeAssignment.Location
を表示する Office 列を追加しました。 (これは、一対ゼロまたは一対一のリレーションシップであるため、関連する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">
各行の他のリンクの直前に Select というラベルの新しい
ActionLink
を追加しました。これは、選択されたインストラクターの ID がIndex
メソッドに送信されるようにします。
アプリケーションを実行し、[Instructors] タブを選択します。ページには、関連する OfficeAssignment
エンティティの Location
プロパティと、関連する OfficeAssignment
エンティティがない場合は、空のテーブル セルが表示されます。
Views\Instructor\Index.cshtml ファイルでは、table
要素を閉じた後 (ファイルの終わり) に、次の強調表示されたコードを追加します。 これは、インストラクターが選択されたときに、インストラクターに関連するコースのリストを表示します。
<td>
@if (item.OfficeAssignment != null)
{
@item.OfficeAssignment.Location
}
</td>
</tr>
}
</table>
@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>
}
このコードでは、ビュー モデルの Courses
プロパティを読み取り、コースのリストを表示します。 また、選択したコースの ID を Index
アクション メソッドに送信する、Select
ハイパーリンクも指定します。
Note
.css ファイルはブラウザーによってキャッシュされます。 アプリケーションの実行時に変更が表示されない場合は、ハード更新を行います (Ctrl キーを押しながら [更新] ボタンをクリックするか、Ctrl キーを押しながら F5 キーを押します)。
ページを実行し、インストラクターを選択します。 選択したインストラクターに割り当てられたコースを表示するグリッドを表示し、各コースに割り当てられた部門の名前を表示します。
追加したコード ブロックの後に、次のコードを追加します。 このコードは、コースを選択したときに、コースに登録されている受講者のリストを表示します。
@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>
@Html.DisplayFor(modelItem => item.Grade)
</td>
</tr>
}
</table>
}
このコードでは、コースに登録された受講生のリストを表示するために、ビュー モデルの Enrollments
プロパティを読み取ります。
ページを実行し、インストラクターを選択します。 次に、コースを選択して、登録済みの受講者とその成績のリストを表示します。
明示的読み込みの追加
InstructorController.cs を開き、Index
メソッドが選択したコースの登録の一覧を取得する方法を確認します。
if (courseID != null)
{
ViewBag.CourseID = courseID.Value;
viewModel.Enrollments = viewModel.Courses.Where(
x => x.CourseID == courseID).Single().Enrollments;
}
インストラクターの一覧を取得したときに、Courses
ナビゲーション プロパティと各コースの Department
プロパティに対して一括読み込みを指定しました。 次に、Courses
コレクションをビュー モデルに配置しました。ここでは、そのコレクション内の 1 つのエンティティから Enrollments
ナビゲーション プロパティにアクセスします。 Course.Enrollments
ナビゲーション プロパティの一括読み込みを指定しなかったため、遅延読み込みの結果として、そのプロパティのデータがページに表示されます。
他の方法でコードを変更せずに遅延読み込みを無効にした場合、コースが実際に持っていた登録数に関係なく、Enrollments
プロパティは null になります。 その場合、Enrollments
プロパティを読み込むには、一括読み込みまたは明示的読み込みを指定する必要があります。 一括読み込みを行う方法を既に確認しました。 明示的読み込みの例を確認するには、Index
メソッドを次のコードに置き換えます。このコードは、Enrollments
プロパティを明示的に読み込みます。 変更されたコードが強調表示されています。
public ActionResult Index(int? id, int? 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 = selectedCourse.Enrollments;
}
return View(viewModel);
}
選択した Course
エンティティを取得した後、新しいコードはそのコースの Enrollments
ナビゲーション プロパティを明示的に読み込みます。
db.Entry(selectedCourse).Collection(x => x.Enrollments).Load();
次に、各 Enrollment
エンティティの関連 Student
エンティティを明示的に読み込みます。
db.Entry(enrollment).Reference(x => x.Student).Load();
Collection
メソッドを使用してコレクション プロパティを読み込みますが、1 つのエンティティのみを保持するプロパティの場合は、Reference
メソッドを使用します。 Instructor インデックス ページを実行すると、データを取得する方法を変更しているにもかかわらず、ページ上で表示される内容に変わりがないことがわかります。
まとめ
3 つの方法 (遅延、一括、明示的) をすべて使用して、関連するデータがナビゲーション プロパティに読み込まれるようになりました。 次のチュートリアルでは、関連データの更新方法を学習します。
他の Entity Framework リソースへのリンクは、ASP.NET データ アクセス コンテンツ マップに関するページにあります。