チュートリアル: ASP.NET MVC アプリで EF を使用して関連データを読み取る

前のチュートリアルでは、School データ モデルを作成しました。 このチュートリアルでは、関連データ (Entity Framework がナビゲーション プロパティに読み込むデータ) の読み取りと表示を行います。

以下の図は、使用するページを示しています。

Screenshot that shows the Courses page with a list of courses.

Instructors_index_page_with_instructor_and_course_selected

完成したプロジェクトのダウンロード

Contoso University のサンプル Web アプリケーションでは、Entity Framework 6 Code First と Visual Studio を使用して ASP.NET MVC 5 アプリケーションを作成する方法を示します。 チュートリアル シリーズについては、シリーズの最初のチュートリアルを参照してください。

このチュートリアルでは、次の作業を行いました。

  • 関連データを読み込む方法を学習する
  • Courses ページを作成する
  • Instructors ページを作成する

前提条件

Entity Framework がエンティティのナビゲーション プロパティに関連データを読み込むには、次の複数の方法があります。

  • 遅延読み込み。 エンティティが最初に読み込まれるときに、関連データは取得されません。 ただし、ナビゲーション プロパティに初めてアクセスしようとすると、そのナビゲーション プロパティに必要なデータが自動的に取得されます。 これにより、エンティティ自体に対して 1 つと、エンティティの関連データを取得するたびに 1 つのように、複数のクエリがデータベースに送信されます。 DbContext クラスは、既定で遅延読み込みを有効にします。

    Lazy_loading_example

  • 一括読み込み。 エンティティが読み取られるときに、関連データがエンティティと共に取得されます。 これは通常、必要なすべてのデータを取得する 1 つの結合クエリになります。 一括読み込みは、Include メソッドを使用して指定します。

    Eager_loading_example

  • 明示的読み込み。 これは遅延読み込みと似ていますが、関連データをコードで明示的に取得する点が異なります。コードの取得は、ナビゲーション プロパティにアクセスしても自動的には発生しません。 関連データを手動で読み込むには、エンティティのオブジェクト状態マネージャー エントリを取得し、コレクションの Collection.Load メソッドを呼び出すか、単一のエンティティを保持するプロパティの Reference.Load メソッドを呼び出します。 (次の例で、Administrator ナビゲーション プロパティを読み込む場合は、Collection(x => x.Courses)Reference(x => x.Administrator) に置き換えます)。通常は、遅延読み込みをオフにした場合にのみ、明示的読み込みを使用します。

    Explicit_loading_example

プロパティ値はすぐには取得されないため、遅延読み込み (lazy loading) と明示的読み込み (explicit loading) はどちらも遅延読み込み (deferred loading) と呼ばれます。

パフォーマンスに関する考慮事項

取得したすべてのエンティティの関連データが必要な場合は、通常、データベースに送信された 1 つのクエリの方が、取得した各エンティティに対する分離したクエリよりも効率的なため、一括読み込みを使用すると、より最適なパフォーマンスが得られます。 たとえば、上記の例で、各部門に 10 個の関連コースがあるとします。 一括読み込みの例では、1 つの (結合) クエリと 1 回のデータベースとのラウンド トリップだけになります。 遅延読み込みと明示的読み込みの例の両方で、11 個のクエリと 11 回のラウンド トリップがデータベースに行われます。 データベースとの余分なラウンド トリップは、特に待ち時間が長いときのパフォーマンスに悪影響をもたらします。

その一方で、一部のシナリオでは、遅延読み込みがより効率的です。 一括読み込みでは、非常に複雑な結合が生成される可能性があり、SQL Server では効率的に処理できません。 または、処理しているエンティティのセットのサブセットのためだけにエンティティのナビゲーション プロパティにアクセスする必要がある場合、一括読み込みでは、必要以上にデータを取得するため、遅延読み込みの方が適切に実行される可能性があります。 パフォーマンスが重要な場合、最適な選択を行うために、両方の方法でパフォーマンスをテストすることをお勧めします。

遅延読み込みでは、パフォーマンスの問題を引き起こすコードをマスクできます。 たとえば、一括読み込みまたは明示的読み込みを指定せず、大量のエンティティを処理し、各イテレーションで複数のナビゲーション プロパティを使用するコードは、非常に非効率的な場合があります (データベースへのラウンド トリップが多いため)。 オンプレミスの SQL Server を使用した開発で優れたパフォーマンスを発揮するアプリケーションでは、待機時間の増加と遅延読み込みが原因で、Azure SQL Database に移動したときにパフォーマンスの問題が発生する可能性があります。 現実的なテスト負荷でデータベース クエリをプロファイリングすると、遅延読み込みが適切かどうかを判断するのに役立ちます。 詳細については、Entity Framework 戦略の解明: 関連データの読み込みの記事と、Entity Framework を使用した SQL Azure へのネットワーク待機時間の短縮の記事を参照してください。

シリアル化前に遅延読み込みを無効にする

シリアル化中に遅延読み込みを有効のままにすると、意図したよりもかなり多くのデータのクエリが実行される可能性があります。 シリアル化は通常、ある型のインスタンスの各プロパティにアクセスすることによって機能します。 プロパティ アクセスによって遅延読み込みがトリガーされ、それらの遅延読み込みされたエンティティがシリアル化されます。 その後、シリアル化プロセスは遅延読み込みされたエンティティの各プロパティにアクセスするため、潜在的に遅延読み込みとシリアル化がさらに促進される可能性があります。 このような連鎖反応を防ぐには、エンティティをシリアル化する前に遅延読み込みをオフにします。

シリアル化は、高度なシナリオのチュートリアルで説明されているように、Entity Framework で使用されるプロキシ クラスによっても複雑になる可能性があります。

シリアル化の問題を回避する 1 つの方法は、「Entity Framework での Web API の使用」チュートリアルで示されているように、エンティティ オブジェクトではなくデータ転送オブジェクト (DTO) をシリアル化することです。

DTO を使用しない場合は、プロキシの作成を無効にすることで、遅延読み込みを無効にし、プロキシの問題を回避できます。

遅延読み込みを無効にするその他の方法を次に示します。

  • 特定のナビゲーション プロパティの場合は、プロパティを宣言するときに virtual キーワードを省略します。

  • すべてのナビゲーション プロパティの場合は、LazyLoadingEnabledfalse に設定し、コンテキスト クラスのコンストラクターに次のコードを配置します。

    this.Configuration.LazyLoadingEnabled = false;
    

Courses ページを作成する

Course エンティティには、コースが割り当てられている部門の Department エンティティを含む、ナビゲーション プロパティが含まれます。 コースのリストに割り当てられた部門の名前を表示するには、Course.Department ナビゲーション プロパティにある Department エンティティから Name プロパティを取得する必要があります。

Student コントローラーに対して前に実行した Entity Framework のスキャフォールディング機能を使用して、ビューと共に MVC 5 コントローラーに同じオプションを使用して、Course エンティティ型の CourseController という名前(CoursesController ではなく) のコントローラー を作成します。

設定 Value
モデル クラス [Course (ContosoUniversity.Models)] を選択します。
データ コンテキスト クラス [SchoolContext (ContosoUniversity.DAL)] を選択します。
コントローラー名 CourseController」と入力します。 ここでも、s を持つ CoursesController ではありません。 [Course (ContosoUniversity.Models)] を選択したら、[コントローラー名] の値が自動的に取り込まれました。 この値を変更する必要があります。

その他を既定値のままにして、コントローラーを追加します。

Controllers\CourseController.cs を開き、Index メソッドを確認します。

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

自動スキャフォールディングでは、Include メソッドを使って、Department ナビゲーション プロパティに一括読み込みを指定しています。

Views\Course\Index.cshtml を開いて、テンプレート コードを次のコードに置き換えます。 変更が強調表示されています。

@model IEnumerable<ContosoUniversity.Models.Course>

@{
    ViewBag.Title = "Courses";
}

<h2>Courses</h2>

<p>
    @Html.ActionLink("Create New", "Create")
</p>
<table class="table">
    <tr>
        <th>
            @Html.DisplayNameFor(model => model.CourseID)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.Title)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.Credits)
        </th>
        <th>
            Department
        </th>
        <th></th>
    </tr>

@foreach (var item in Model) {
    <tr>
        <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>
        <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>
    </tr>
}

</table>

スキャフォールディング コードに、次の変更を行いました。

  • 見出しが Index から Courses に変更されました。
  • CourseID プロパティ値を示す Number 列が追加されました。 既定では、主キーは、通常、エンド ユーザーにとって意味がないため、スキャフォールディングされません。 ただし、このケースでは、主キーは意味があり、表示する必要があります。
  • Department 列が右側に移動され、見出しが変更されました。 スキャフォールディングでは Department エンティティの Name プロパティを正しく表示することを選択しましたが、ここでは Course ページで列見出しが Name ではなく 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; }
    }
}

Instructor コントローラーとビューを作成する

EF の読み取り/書き込みアクションを使用して InstructorController (InstructorsController ではなく) コントローラーを作成します。

設定 Value
モデル クラス [Instructor (ContosoUniversity.Models)] を選択します。
データ コンテキスト クラス [SchoolContext (ContosoUniversity.DAL)] を選択します。
コントローラー名 InstructorController」と入力します。 ここでも、s を持つ InstructorsController ではありません。 [Course (ContosoUniversity.Models)] を選択したら、[コントローラー名] の値が自動的に取り込まれました。 この値を変更する必要があります。

その他を既定値のままにして、コントローラーを追加します。

Controllers\InstructorController.cs を開き、ViewModels 名前空間の using ステートメントを追加します。

using ContosoUniversity.ViewModels;

Index メソッドのスキャフォールディングされたコードは、OfficeAssignment ナビゲーション プロパティに対してのみ一括読み込みを指定します。

public ActionResult 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.ID == 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 ハイパーリンクによって指定されます。

このコードは、ビュー モデルのインスタンスを作成し、インストラクターのリストに配置することから始めます。 コードでは、Instructor.OfficeAssignmentInstructor.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.ID == id.Value).Single().Courses;
}

Where メソッドはコレクションを返しますが、このケースでは、そのメソッドに渡された条件は、返されている Instructor エンティティの 1 つのみになります。 Single メソッドを実行すると、コレクションが、エンティティの Courses プロパティへのアクセス権を付与する 1 つの Instructor エンティティに変換されます。

コレクションに項目が 1 つのみであることがわかっている場合、コレクションで Single メソッドを使用します。 渡されるコレクションが空になる場合、または複数の項目がある場合、Single メソッドから例外がスローされます。 代わりに、コレクションが空の場合に既定値 (この場合は null) を返す SingleOrDefault を使用します。 ただし、この場合は引き続き例外となり (null 参照で Courses プロパティを見つけようとして)、例外メッセージでは問題の原因があまり明確に示されません。 Single メソッドを呼び出す場合、個別に Where メソッドを呼び出す代わりに、Where 条件で渡すこともできます。

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

これは次のコードの代わりに使用します。

.Where(I => i.ID == 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 class="table">
    <tr>
        <th>Last Name</th>
        <th>First Name</th>
        <th>Hire Date</th>
        <th>Office</th>
        <th></th>
    </tr>

    @foreach (var item in Model.Instructors)
    {
        string selectedRow = "";
        if (item.ID == ViewBag.InstructorID)
        {
            selectedRow = "success";
        }
        <tr class="@selectedRow">
            <td>
                @Html.DisplayFor(modelItem => item.LastName)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.FirstMidName)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.HireDate)
            </td>
            <td>
                @if (item.OfficeAssignment != null)
                {
                    @item.OfficeAssignment.Location
                }
            </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>
        </tr>
    }

    </table>

既存のコードに次の変更を行いました。

  • モデル クラスが InstructorIndexData に変更されました。

  • Index のページ タイトルが Instructors に変更されました。

  • item.OfficeAssignment が null ではない場合にのみ item.OfficeAssignment.Location を表示する Office 列を追加しました。 (これは、一対ゼロまたは一対一のリレーションシップであるため、関連する OfficeAssignment エンティティがない場合があります)。

    <td> 
        @if (item.OfficeAssignment != null) 
        { 
            @item.OfficeAssignment.Location  
        } 
    </td>
    
  • 選択したインストラクターの tr 要素に class="success" を動的に追加するコードを追加しました。 これは、ブートストラップ クラスを使用して、選択した行の背景色を設定します。

    string selectedRow = ""; 
    if (item.InstructorID == ViewBag.InstructorID) 
    { 
        selectedRow = "success"; 
    } 
    <tr class="@selectedRow" valign="top">
    
  • 各行の他のリンクの直前に Select というラベルの新しい ActionLink を追加しました。これは、選択されたインストラクターの ID が Index メソッドに送信されるようにします。

アプリケーションを実行し、[Instructors] タブを選択します。ページには、関連する OfficeAssignment エンティティの Location プロパティと、関連する OfficeAssignment エンティティがない場合は、空のテーブル セルが表示されます。

Views\Instructor\Index.cshtml ファイルでは、table 要素を閉じた後 (ファイルの終わり) に、次のコードを追加します。 このコードでは、インストラクターが選択されたときに、インストラクターに関連するコースのリストを表示します。

@if (Model.Courses != null)
{
    <h3>Courses Taught by Selected Instructor</h3>
    <table class="table">
        <tr>
            <th></th>
            <th>Number</th>
            <th>Title</th>
            <th>Department</th>
        </tr>

        @foreach (var item in Model.Courses)
        {
            string selectedRow = "";
            if (item.CourseID == ViewBag.CourseID)
            {
                selectedRow = "success";
            }
            <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 ハイパーリンクも指定します。

ページを実行し、インストラクターを選択します。 選択したインストラクターに割り当てられたコースを表示するグリッドを表示し、各コースに割り当てられた部門の名前を表示します。

追加したコード ブロックの後に、次のコードを追加します。 このコードは、コースを選択したときに、コースに登録されている受講者のリストを表示します。

@if (Model.Enrollments != null)
{
    <h3>
        Students Enrolled in Selected Course
    </h3>
    <table class="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.ID == id.Value).Single().Courses;
    }
    
    if (courseID != null)
    {
        ViewBag.CourseID = courseID.Value;
        // Lazy loading
        //viewModel.Enrollments = viewModel.Courses.Where(
        //    x => x.CourseID == courseID).Single().Enrollments;
        // Explicit loading
        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 インデックス ページを実行すると、データを取得する方法を変更しているにもかかわらず、ページ上で表示される内容に変わりがないことがわかります。

コードを取得する

完成したプロジェクトのダウンロード

その他のリソース

他の Entity Framework リソースへのリンクは、「ASP.NET データ アクセス - 推奨リソース」にあります。

次のステップ

このチュートリアルでは、次の作業を行いました。

  • 関連データを読み込む方法を学習した
  • Courses ページを作成した
  • Instructors ページを作成した

関連データを更新する方法について学習するには、次の記事に進んでください。