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
基底クラスから派生する Instructor
と Student
のクラスを作成できます。 Entity Framework 内のエンティティ間で同じ種類の継承構造を作成できます。
チュートリアルのこの部分では、新しい Web ページを作成しません。 代わりに、派生エンティティをデータ モデルに追加し、新しいエンティティを使用するように既存のページを変更します。
Table-Per-Hierarchy と Table-Per-Type 継承
データベースは、関連オブジェクトに関する情報を 1 つのテーブルまたは複数のテーブルに格納できます。 たとえば、School
データベースの Person
テーブルには、学生と講師の両方に関する情報が 1 つのテーブルに含まれています。 列の中には、講師のみに適用されるもの (HireDate
)、学生のみに適用されるもの (EnrollmentDate
)、両方に適用されるもの (LastName
、FirstName
) があります。
Person
エンティティを継承する Instructor
および Student
エンティティを作成するように Entity Framework を構成できます。 1 つのデータベース テーブルからエンティティの継承構造を生成するこのパターンは、Table-per-Hierarchy (TPH) 継承と呼ばれます。
コースの場合、School
データベースは別のパターンを使用します。 オンライン コースとオンサイト コースは別のテーブルに格納され、それぞれに Course
テーブルを指す外部キーがあります。 両方のコースの型に共通の情報は、Course
テーブルにのみ格納されます。
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 ファイルを開き、デザイナーの空いている領域を右クリックして、[追加] を選び、次に [エンティティ] を選びます。
[エンティティの追加] ダイアログ ボックスで、エンティティに Instructor
という名前を付け、その [基本データ型] オプションを Person
に設定します。
OK をクリックします。 デザイナーが、Person
エンティティから派生する Instructor
エンティティを作成します。 新しいエンティティにはまだプロパティがありません。
この手順を繰り返して、同じく Person
から派生する Student
エンティティを作成します。
講師のみが採用日を持っているため、そのプロパティを Person
エンティティから Instructor
エンティティに移動する必要があります。 Person
エンティティで、HireDate
プロパティを右クリックし、[切り取り] をクリックします。 次に、Instructor
エンティティの [プロパティ] を右クリックし、[貼り付け] をクリックします。
Instructor
エンティティの採用日を null にすることはできません。 HireDate
プロパティを右クリックし、[プロパティ] をクリックし、[プロパティ] ウィンドウで Nullable
を False
に変更します。
この手順を繰り返して、EnrollmentDate
プロパティを Person
エンティティから Student
エンティティに移動します。 EnrollmentDate
プロパティの Nullable
を False
に設定していることも確認します。
Person
エンティティには、(移動しないナビゲーション プロパティを除いて) Instructor
および Student
エンティティに共通のプロパティのみが含まれるため、エンティティは継承構造の基本エンティティとしてのみ使用できます。 そのため、独立したエンティティとして扱われないようにする必要があります。 Person
エンティティを右クリックし、[プロパティ] を選んでから、[プロパティ] ウィンドウで、Abstract プロパティの値を True に変更します。
Instructor および Student エンティティの Person テーブルへのマッピング
次に、データベース内の Instructor
エンティティと Student
エンティティを区別する方法を Entity Framework に指示する必要があります。
Instructor
エンティティを右クリックし、[テーブル マッピング] を選びます。 [マッピングの詳細] ウィンドウで、[テーブルまたはビューの追加] をクリックし、[Person] を選びます。
[条件の追加] をクリックし、[HireDate] を選びます。
[演算子] を [Is] に変更し、[値/プロパティ] を [Not Null] に変更します。
Students
エンティティに対してこの手順を繰り返し、EnrollmentDate
列が null でない場合にこのエンティティが Person
テーブルにマップされることを指定します。 次に、データ モデルを保存して閉じます。
新しいエンティティをクラスとして作成し、デザイナーで使用できるようにするために、プロジェクトをビルドします。
Instructor および Student エンティティの使用
学生および講師のデータを扱う Web ページを作成したときは、それらを Person
エンティティ セットにデータバインドし、HireDate
または EnrollmentDate
プロパティでフィルター処理して、返されるデータを学生または講師に制限しました。 ただし、各データ ソース コントロールを Person
エンティティ セットにバインドするときに、Student
または Instructor
エンティティ型のみを選ぶように指定できるようになりました。 Entity Framework は、Person
エンティティ セット内の学生と講師を区別する方法を認識しているため、手動で入力した Where
プロパティ設定を削除してこれを行うことができます。
Visual Studio デザイナーでは、次の例に示すように、Configure Data Source
ウィザードの EntityTypeFilter ドロップダウン ボックスで、EntityDataSource
コントロールが選ぶエンティティ型を指定できます。
また、次の例に示すように、[プロパティ] ウィンドウで、不要になった Where
句の値を削除できます。
ただし、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
コントロールでは指定されたエンティティ型のみが選ばれるようになります。 Student
と Instructor
の両方のエンティティ型を取得する場合は、この属性を設定しません。 (読み取り専用データ アクセスにコントロールを使用している場合にのみ、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 + '%'" >
ページを実行して、以前と同じように機能することを確認します。
前のチュートリアルで作成した以下のページを更新して、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>
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>
Instructors.aspx と InstructorsCourses.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>
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>
これらの変更の結果、Contoso University アプリケーションの保守性がいくつかの点で改善されました。 選択と検証のロジックを UI レイヤー (.aspx マークアップ) から移動し、データ アクセス レイヤーの不可欠な部分にしました。 これは、データベース スキーマまたはデータ モデルに対して将来行われる可能性のある変更からアプリケーション コードを分離するのに役立ちます。 たとえば、学生が教師の補助として採用され、採用日を持つようになる可能性があると判断することもあります。 その後、新しいプロパティを追加して学生と講師を区別し、データ モデルを更新できます。 学生の採用日を表示する場合を除き、Web アプリケーション内のコードを変更する必要はありません。 Instructor
および Student
エンティティを追加するもう 1 つの利点は、実際に学生や講師である Person
オブジェクトを参照する場合よりもコードが理解しやすいことです。
Entity Framework で継承パターンを実装する 1 つの方法を説明してきました。 次のチュートリアルでは、Entity Framework がデータベースにアクセスする方法をより詳細に制御するために、ストアド プロシージャを使用する方法について説明します。