Entity Framework 4.0 Database First と ASP.NET 4 Web Forms の概要 - 第 6 部

著者: Tom Dykstra

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

Table-Per-Hierarchy 継承の実装

前のチュートリアルでは、リレーションシップの追加と削除を行い、既存のエンティティにリレーションシップを持つ新しいエンティティを追加することで、関連データを操作しました。 このチュートリアルでは、データ モデルで継承を実装する方法を示します。

オブジェクト指向プログラミングでは、継承を使用して、関連するクラスの操作を容易にすることができます。 たとえば、Person 基底クラスから派生する InstructorStudent のクラスを作成できます。 Entity Framework 内のエンティティ間で同じ種類の継承構造を作成できます。

チュートリアルのこの部分では、新しい Web ページを作成しません。 代わりに、派生エンティティをデータ モデルに追加し、新しいエンティティを使用するように既存のページを変更します。

Table-Per-Hierarchy と Table-Per-Type 継承

データベースは、関連オブジェクトに関する情報を 1 つのテーブルまたは複数のテーブルに格納できます。 たとえば、School データベースの Person テーブルには、学生と講師の両方に関する情報が 1 つのテーブルに含まれています。 列の中には、講師のみに適用されるもの (HireDate)、学生のみに適用されるもの (EnrollmentDate)、両方に適用されるもの (LastNameFirstName) があります。

image11

Person エンティティを継承する Instructor および Student エンティティを作成するように Entity Framework を構成できます。 1 つのデータベース テーブルからエンティティの継承構造を生成するこのパターンは、Table-per-Hierarchy (TPH) 継承と呼ばれます。

コースの場合、School データベースは別のパターンを使用します。 オンライン コースとオンサイト コースは別のテーブルに格納され、それぞれに Course テーブルを指す外部キーがあります。 両方のコースの型に共通の情報は、Course テーブルにのみ格納されます。

image12

OnlineCourse および OnsiteCourse エンティティが Course エンティティから継承されるように Entity Framework データ モデルを構成できます。 型ごとに個別のテーブルからエンティティ継承構造を生成し、各個別のテーブルがすべての型に共通のデータを格納するテーブルを参照するこのパターンは、Table-Per-Type (TPT) 継承と呼ばれます。

TPH 継承パターンでは、一般的に TPT 継承パターンよりも高いパフォーマンスを Entity Framework で実現します。これは、TPT パターンの結果として複雑な結合クエリになる可能性があるためです。 このチュートリアルでは、TPH 継承を実装する方法を示します。 これを行うには、以下の手順を実行します。

  • Person から派生する Instructor および Student エンティティ型を作成します。
  • 派生エンティティに関連するプロパティを Person エンティティから派生エンティティに移動します。
  • 派生型のプロパティに制約を設定します。
  • Person エンティティを抽象エンティティにします。
  • Person 行がその派生型を表すかどうかを判断する方法を指定する条件を使用して、各派生エンティティを Person テーブルにマップします。

Instructor および Student エンティティの追加

SchoolModel.edmx ファイルを開き、デザイナーの空いている領域を右クリックして、[追加] を選び、次に [エンティティ] を選びます。

image01

[エンティティの追加] ダイアログ ボックスで、エンティティに Instructor という名前を付け、その [基本データ型] オプションを Person に設定します。

image02

OK をクリックします。 デザイナーが、Person エンティティから派生する Instructor エンティティを作成します。 新しいエンティティにはまだプロパティがありません。

image03

この手順を繰り返して、同じく Person から派生する Student エンティティを作成します。

講師のみが採用日を持っているため、そのプロパティを Person エンティティから Instructor エンティティに移動する必要があります。 Person エンティティで、HireDate プロパティを右クリックし、[切り取り] をクリックします。 次に、Instructor エンティティの [プロパティ] を右クリックし、[貼り付け] をクリックします。

image04

Instructor エンティティの採用日を null にすることはできません。 HireDate プロパティを右クリックし、[プロパティ] をクリックし、[プロパティ] ウィンドウで NullableFalse に変更します。

image05

この手順を繰り返して、EnrollmentDate プロパティを Person エンティティから Student エンティティに移動します。 EnrollmentDate プロパティの NullableFalse に設定していることも確認します。

Person エンティティには、(移動しないナビゲーション プロパティを除いて) Instructor および Student エンティティに共通のプロパティのみが含まれるため、エンティティは継承構造の基本エンティティとしてのみ使用できます。 そのため、独立したエンティティとして扱われないようにする必要があります。 Person エンティティを右クリックし、[プロパティ] を選んでから、[プロパティ] ウィンドウで、Abstract プロパティの値を True に変更します。

image06

Instructor および Student エンティティの Person テーブルへのマッピング

次に、データベース内の Instructor エンティティと Student エンティティを区別する方法を Entity Framework に指示する必要があります。

Instructor エンティティを右クリックし、[テーブル マッピング] を選びます。 [マッピングの詳細] ウィンドウで、[テーブルまたはビューの追加] をクリックし、[Person] を選びます。

image07

[条件の追加] をクリックし、[HireDate] を選びます。

image09

[演算子][Is] に変更し、[値/プロパティ][Not Null] に変更します。

image10

Students エンティティに対してこの手順を繰り返し、EnrollmentDate 列が null でない場合にこのエンティティが Person テーブルにマップされることを指定します。 次に、データ モデルを保存して閉じます。

新しいエンティティをクラスとして作成し、デザイナーで使用できるようにするために、プロジェクトをビルドします。

Instructor および Student エンティティの使用

学生および講師のデータを扱う Web ページを作成したときは、それらを Person エンティティ セットにデータバインドし、HireDate または EnrollmentDate プロパティでフィルター処理して、返されるデータを学生または講師に制限しました。 ただし、各データ ソース コントロールを Person エンティティ セットにバインドするときに、Student または Instructor エンティティ型のみを選ぶように指定できるようになりました。 Entity Framework は、Person エンティティ セット内の学生と講師を区別する方法を認識しているため、手動で入力した Where プロパティ設定を削除してこれを行うことができます。

Visual Studio デザイナーでは、次の例に示すように、Configure Data Source ウィザードの EntityTypeFilter ドロップダウン ボックスで、EntityDataSource コントロールが選ぶエンティティ型を指定できます。

image13

また、次の例に示すように、[プロパティ] ウィンドウで、不要になった Where 句の値を削除できます。

image14

ただし、ContextTypeName 属性を使用するように EntityDataSource コントロールのマークアップを変更したため、既に作成した EntityDataSource コントロールに対してデータ ソースの構成ウィザードを実行することはできません。 そのため、代わりにマークアップを変更することで必要な変更を行います。

Students.aspx ページを開きます。 StudentsEntityDataSource コントロールで、Where 属性を削除し、EntityTypeFilter="Student" 属性を追加します。 マークアップは、次の例のようになります。

<asp:EntityDataSource ID="StudentsEntityDataSource" runat="server" 
        ContextTypeName="ContosoUniversity.DAL.SchoolEntities" EnableFlattening="False"
        EntitySetName="People" EntityTypeFilter="Student"
        Include="StudentGrades"
        EnableDelete="True" EnableUpdate="True" 
        OrderBy="it.LastName" >
    </asp:EntityDataSource>

EntityTypeFilter 属性を設定すると、EntityDataSource コントロールでは指定されたエンティティ型のみが選ばれるようになります。 StudentInstructor の両方のエンティティ型を取得する場合は、この属性を設定しません。 (読み取り専用データ アクセスにコントロールを使用している場合にのみ、1 つの EntityDataSource コントロールで複数のエンティティ型を取得する選択肢があります。EntityDataSource コントロールを使用してエンティティの挿入、更新、または削除を行っており、バインドされているエンティティ セットに複数の型を含めることができる場合は、1 つのエンティティ型のみを操作できるため、この属性を設定する必要があります)

SearchEntityDataSource コントロールに対してこの手順を繰り返します。ただし、プロパティを完全に削除するのではなく、Student エンティティを選ぶ Where 属性の部分のみを削除します。 コントロールの開始タグは、次の例のようになります。

<asp:EntityDataSource ID="SearchEntityDataSource" runat="server" 
        ContextTypeName="ContosoUniversity.DAL.SchoolEntities" EnableFlattening="False" 
        EntitySetName="People" EntityTypeFilter="Student"
        Where="it.FirstMidName Like '%' + @StudentName + '%' or it.LastName Like '%' + @StudentName + '%'" >

ページを実行して、以前と同じように機能することを確認します。

image15

前のチュートリアルで作成した以下のページを更新して、Person エンティティではなく新しい Student および Instructor エンティティを使用し、その後、それらを実行して以前と同様に機能することを確認します。

  • StudentsAdd.aspx で、EntityTypeFilter="Student"StudentsEntityDataSource コントロールに追加します。 マークアップは、次の例のようになります。

    <asp:EntityDataSource ID="StudentsEntityDataSource" runat="server" 
            ContextTypeName="ContosoUniversity.DAL.SchoolEntities" EnableFlattening="False"
            EntitySetName="People" EntityTypeFilter="Student"
            EnableInsert="True" 
        </asp:EntityDataSource>
    

    image16

  • About.aspx で、EntityTypeFilter="Student"StudentStatisticsEntityDataSource コントロールに追加し、Where="it.EnrollmentDate is not null" を削除します。 マークアップは、次の例のようになります。

    <asp:EntityDataSource ID="StudentStatisticsEntityDataSource" runat="server" 
            ContextTypeName="ContosoUniversity.DAL.SchoolEntities" EnableFlattening="False" 
            EntitySetName="People" EntityTypeFilter="Student"
            Select="it.EnrollmentDate, Count(it.EnrollmentDate) AS NumberOfStudents"
            OrderBy="it.EnrollmentDate" GroupBy="it.EnrollmentDate" >
        </asp:EntityDataSource>
    

    image17

  • Instructors.aspxInstructorsCourses.aspx で、EntityTypeFilter="Instructor"InstructorsEntityDataSource コントロールに追加し、Where="it.HireDate is not null" を削除します。 Instructors.aspx のマークアップは、次の例のようになります。

    <asp:EntityDataSource ID="InstructorsEntityDataSource" runat="server" 
                ContextTypeName="ContosoUniversity.DAL.SchoolEntities" EnableFlattening="false"
                EntitySetName="People" EntityTypeFilter="Instructor" 
                Include="OfficeAssignment" 
                EnableUpdate="True">
            </asp:EntityDataSource>
    

    image18

    InstructorsCourses.aspx のマークアップは、次の例のようになります。

    <asp:EntityDataSource ID="InstructorsEntityDataSource" runat="server" 
            ContextTypeName="ContosoUniversity.DAL.SchoolEntities" EnableFlattening="False" 
            EntitySetName="People" EntityTypeFilter="Instructor" 
            Select="it.LastName + ',' + it.FirstMidName AS Name, it.PersonID">
        </asp:EntityDataSource>
    

    image19

これらの変更の結果、Contoso University アプリケーションの保守性がいくつかの点で改善されました。 選択と検証のロジックを UI レイヤー (.aspx マークアップ) から移動し、データ アクセス レイヤーの不可欠な部分にしました。 これは、データベース スキーマまたはデータ モデルに対して将来行われる可能性のある変更からアプリケーション コードを分離するのに役立ちます。 たとえば、学生が教師の補助として採用され、採用日を持つようになる可能性があると判断することもあります。 その後、新しいプロパティを追加して学生と講師を区別し、データ モデルを更新できます。 学生の採用日を表示する場合を除き、Web アプリケーション内のコードを変更する必要はありません。 Instructor および Student エンティティを追加するもう 1 つの利点は、実際に学生や講師である Person オブジェクトを参照する場合よりもコードが理解しやすいことです。

Entity Framework で継承パターンを実装する 1 つの方法を説明してきました。 次のチュートリアルでは、Entity Framework がデータベースにアクセスする方法をより詳細に制御するために、ストアド プロシージャを使用する方法について説明します。