チュートリアル : Table-Per-Hierarchy 継承のマッピング
このトピックでは、Entity Data Model (EDM) で概念モデルを変更することによって Table-Per-Hierarchy 継承を実装する方法について説明します。Table-Per-Hierarchy 継承では、1 つのデータベース テーブルを使用して、継承階層のすべてのエンティティ型のデータを維持します。EDM での継承の実装の詳細については、「継承 (EDM)」を参照してください。
このチュートリアルでは、CourseManager アプリケーションで使用する EDM を変更することによって、Table-Per-Hierarchy 継承を実装します (詳細は、このトピックの「前提条件」を参照してください)。
CourseManager EDM の Person エンティティ型には、HireDate と EnrollmentDate という 2 つのプロパティがあります。これらのプロパティは、Person から継承した新しいエンティティ型 (それぞれ Instructor と Student) に属することができます。以下の手順は、この場合に Table-Per-Hierarchy 継承を実装する方法をまとめたものです。このチュートリアルでは、これらの手順について詳しく説明します。
Instructor と Student という 2 つの新しいエンティティ型を作成します。
新しいエンティティ型の基本データ型をそれぞれ Person に設定します。
HireDate プロパティを Person から Instructor に移動し、EnrollmentDate プロパティを Person から Student に移動します。
Person エンティティ型を抽象型にします。
HireDate Is Not Null と EnrollmentDate Is Null という 2 つの条件を設定して、Instructor エンティティ型を Person テーブルにマップします。
EnrollmentDate Is Not Null と HireDate Is Null という 2 つの条件を設定して、Student エンティティ型を Person テーブルにマップします。
前提条件
このチュートリアルを完了するには、CourseManager アプリケーションを構築する必要があります。詳細情報と手順については、「Entity Framework クイック スタート」を参照してください。このアプリケーションを構築した後に、Table-Per-Hierarchy 継承を実装して EDM を変更します。その後、選択したコースの登録を表示できるようにアプリケーションの機能を拡張します。
[!メモ]
このドキュメントのチュートリアルのトピックの多くは CourseManager アプリケーションを開始点としているため、元の CourseManager コードを編集するのではなく、このチュートリアル用に CourseManager アプリケーションのコピーを使用することをお勧めします。
このチュートリアルでは、Visual Studio、.NET Framework、および Visual C# または Visual Basic のプログラミングの基本的なスキルがある読者を想定しています。
Table-Per-Hierarchy 継承の実装
この手順では、SchoolModel EDM の概念モデルを変更することによって Table-Per-Hierarchy 継承を実装します。
Table-Per-Hierarchy 継承を実装するには
Visual Studio で CourseManager ソリューションを開きます。
ソリューション エクスプローラで、School.edmx ファイルをダブルクリックします。
ADO.NET Entity Data Model デザイナ (エンティティ デザイナ) で School.edmx ファイルが開きます。
エンティティ デザイナのデザイン画面の空の領域を右クリックし、[追加] をポイントして [エンティティ] をクリックします。
[新しいエンティティ] ダイアログ ボックスが表示されます。
[エンティティ名] に「Instructor」と入力し、[基本データ型] ボックスの一覧から [Person] を選択します。
[OK] をクリックします。
新しいエンティティ型が作成され、デザイン画面に表示されます。
手順 3. ~ 5. を繰り返します。ただし、2 回目は [エンティティ名] に「Student」と入力します。
これで、2 つの新しいエンティティ型 Instructor と Student がデザイン画面に表示されます。新しいエンティティ型から Person エンティティ型に向かう矢印は、Person が新しいエンティティ型の基本データ型であることを示します。
Person エンティティ型の HireDate プロパティ ([スカラ プロパティ] の下) を右クリックします。[切り取り] をクリックします。
Instructor エンティティ型の [スカラ プロパティ] を右クリックし、[貼り付け] をクリックします。
HireDate プロパティを右クリックし、[プロパティ] をクリックします。
[プロパティ] ウィンドウで、[Null 許容] プロパティを false に設定します。
手順 7. ~ 10. を繰り返します。ただし今回は、Person エンティティ型の EnrollmentDate プロパティを切り取り、Student エンティティ型に貼り付けます。
Person エンティティ型を選択します。[プロパティ] ウィンドウで、[抽象] プロパティを true に設定します。
エンティティ型を抽象型として定義するとその型の既存の関数マッピングがすべて削除されることを通知するメッセージ ボックスが表示されます。[OK] をクリックします。
[!メモ]
通常、Table-Per-Hierarchy のシナリオをモデル化するために抽象型を使用する必要はありません。ここでは、EDM での使用例を示すために抽象型を使用しています。
[!メモ]
この手順の以降の手順では、[マッピングの詳細] ウィンドウを使用します。このウィンドウが表示されていない場合は、デザイン画面を右クリックして [マッピングの詳細] をクリックします。
[マッピングの詳細] ウィンドウで、Instructor エンティティ型を選択し、[<テーブルまたはビューの追加>] をクリックします。
[<テーブルまたはビューの追加>] フィールドは、選択したエンティティをマップできるテーブルまたはビューを示すドロップダウン リストになります。
ドロップダウン リストから [Person] を選択します。
[マッピングの詳細] ウィンドウが更新され、既定の列マッピング、および条件を追加するオプションが表示されます。
[<条件の追加>] をクリックします。
[<条件の追加>] フィールドは、条件を設定できる列を示すドロップダウン リストになります。
ドロップダウン リストから [HireDate] を選択します。
[<条件の追加>] フィールドがもう 1 つ表示されます。
[マッピングの詳細] ウィンドウの [演算子] 列で、ドロップダウン リストから [Is] を選択します。
[プロパティ/値] 列で [NULL 以外] を選択します。
[<条件の追加>] をクリックします。
ドロップダウン リストから [EnrollmentDate] を選択します。
[演算子] 列で、ドロップダウン リストから [Is] を選択します。
[プロパティ/値] 列で [NULL] を選択します。
[!メモ]
条件で値/プロパティを使用する場合、Is Null または Is Not Null 比較を使用しないと、その値/プロパティをエンティティ プロパティにすることはできません。
Student エンティティ型について、手順 13. ~ 22. を繰り返します。ただし、条件は HireDate Is Null と EnrollmentDate Is Not Null にします。
これで、Table-Per-Hierarchy 継承が実装されました。
ユーザー インターフェイスの構築
次に、Enrollment フォームを読み込んで表示するボタンを CourseViewer フォームに追加します。さらに、2 つの ComboBox コントロールと 1 つの ListBox コントロールを Enrollment フォームに追加します。1 つ目の ComboBox では、部門を選択できます。2 つ目の ComboBox では、1 つ目の ComboBox で選択した部門に基づいてコースを選択できます。選択したコースの学生およびインストラクタの一覧が ListBox コントロールに表示されます。
ユーザー インターフェイスを構築するには
ソリューション エクスプローラで [CourseManager] プロジェクトを右クリックし、[追加] をポイントして [新しい項目] をクリックします。
[新しい項目の追加] ダイアログ ボックスが表示されます。
[Windows フォーム] を選択し、フォーム名を (使用している言語に応じて) Enrollment.vb または Enrollment.cs に設定して、[追加] をクリックします。
新しいフォームがプロジェクトに追加され、フォーム デザイナで開かれます。フォーム名が Enrollment に、テキストが Enrollment に設定されます。
ComboBox コントロールをツールボックスからフォームにドラッグし、[プロパティ] ウィンドウで名前を departmentList に設定します。
別の ComboBox コントロールをフォームにドラッグし、名前を courseList に設定します。
Listbox コントロールをツールボックスからフォームにドラッグします。[プロパティ] ウィンドウで、名前を studentList に設定します。
ソリューション エクスプローラで、CourseViewer.cs または CourseViewer.vb をダブルクリックします。
CourseViewer フォームのデザイン ビューが表示されます。
Button コントロールをツールボックスから CourseViewer フォームにドラッグします。
[プロパティ] ウィンドウで、ボタンの名前を viewEnrollment に設定し、ボタンのテキストを View Enrollment に設定します。
[viewEnrollment] ボタンをダブルクリックします。
CourseViewer フォームの分離コード ファイルが開きます。
viewEnrollment_click イベント ハンドラに次のコードを追加します。
Dim enrollmentForm As New Enrollment() enrollmentForm.Visible = True
Enrollment enrollmentForm = new Enrollment(); enrollmentForm.Visible = true;
ユーザー インターフェイスが完成しました。
EDM に対するクエリ
この手順では、EDM に対してクエリを実行し、結果を Windows フォーム コントロールにバインドします。オブジェクトをコントロールにバインドする方法の詳細については、「コントロールへのオブジェクトのバインド (Entity Framework)」を参照してください。
EDM に対してクエリを実行するには
フォーム デザイナで Enrollment フォームが開いた状態で、Enrollment フォームをダブルクリックします。
Enrollment フォームの分離コード ファイルが開きます。
次の using (C#) ステートメントまたは Imports (Visual Basic) ステートメントを追加して、EDM 名前空間を参照します。
Imports System.Data.Objects Imports System.Data.Objects.DataClasses
using System.Data.Objects; using System.Data.Objects.DataClasses;
データ コンテキストを表すプロパティを Enrollment クラスに追加します。
' Create an ObjectContext instance based on SchoolEntity. Private schoolContext As SchoolEntities
// Create an ObjectContext instance based on SchoolEntity. private SchoolEntities schoolContext;
Enrollment_Load イベント ハンドラで、オブジェクト コンテキストを初期化して、すべての部門および関連コースの情報を返すクエリに ComboBox コントロールをバインドするコードを追加します。
' Initialize the ObjectContext. schoolContext = New SchoolEntities() ' Define a query that returns all Department objects and ' related Course objects, ordered by name. Dim departmentQuery As ObjectQuery(Of Department) = _ schoolContext.Department.Include("Course") _ .OrderBy("it.Name") ' Bind the ComboBox control to the query, which is ' executed during data binding. Me.departmentList.DataSource = departmentQuery _ .Execute(MergeOption.OverwriteChanges) Me.departmentList.DisplayMember = "Name"
// Initialize the ObjectContext. schoolContext = new SchoolEntities(); // Define a query that returns all Department objects // and related Course objects, ordered by name. ObjectQuery<Department> departmentQuery = schoolContext.Department.Include("Course") .OrderBy("it.Name"); // Bind the ComboBox control to the query, which is // executed during data binding. this.departmentList.DataSource = departmentQuery .Execute(MergeOption.OverwriteChanges); this.departmentList.DisplayMember = "Name";
Enrollment フォームのデザイン ビューに戻り、departmentListComboBox コントロールをダブルクリックします。
departmentList_SelectedIndexChanged イベント ハンドラが分離コード ファイルで作成されます。
イベント ハンドラに、選択した Department の Course プロパティに courseListComboBox をバインドするコードを追加します。
' Get the object for the selected department Dim department As Department = CType(Me.departmentList _ .SelectedItem, Department) ' Bind the ComboBox control Course properties of ' the selected Department. courseList.DataSource = department.Course courseList.DisplayMember = "Title"
// Get the object for the selected department. Department department = (Department)this.departmentList.SelectedItem; // Bind the ComboBox control Course properties of // the selected Department. courseList.DataSource = department.Course; courseList.DisplayMember = "Title";
Enrollment フォームのデザイン ビューに戻り、courseListComboBox コントロールをダブルクリックします。
courseList_SelectedIndexChanged イベント ハンドラが分離コード ファイルで作成されます。
イベント ハンドラに、学生の一覧を ListBox に表示するコードを追加します。
Me.studentList.Items.Clear() ' Get the CourseID from the selected Course. Dim course As Course = CType(Me.courseList.SelectedItem, _ Course) Dim courseId As Integer = course.CourseID ' Get all CourseGrade objects for the supplied CourseID Dim studentQuery As Course = schoolContext.Course.Where _ ("it.CourseID = @courseID", New ObjectParameter _ ("courseID", courseId)).Include("CourseGrade"). _ FirstOrDefault() ' Get all students for each CourseGrade For Each cg As CourseGrade In studentQuery.CourseGrade cg.PersonReference.Load() studentList.Items.Add(cg.Person.LastName + ", " + _ cg.Person.FirstName) Next studentList.Items.Add(" ") ' Get all Instructor types for the supplied CourseID Dim instructorQuery As Course = schoolContext.Course. _ Where("it.CourseID = @courseID", New ObjectParameter _ ("courseID", courseId)).Include("Person") _ .FirstOrDefault() ' Display each instructor for the specified Course For Each Instructor As Instructor In instructorQuery.Person Me.studentList.Items.Add("Instructor: " + Instructor. _ LastName + ", " + Instructor.FirstName) Next
studentList.Items.Clear(); // Get the CourseID from the selected Course. Course course = (Course)courseList.SelectedItem; int courseId = course.CourseID; // Get all CourseGrade types for the supplied CourseID Course studentQuery = schoolContext.Course.Where( "it.CourseID = @courseID", new ObjectParameter ("courseID", courseId)).Include("CourseGrade"). FirstOrDefault(); // Get all students for each CourseGrade foreach (CourseGrade cg in studentQuery.CourseGrade) { cg.PersonReference.Load(); studentList.Items.Add(cg.Person.LastName + ", " + cg.Person.FirstName); } studentList.Items.Add(" "); // Get all Instructor types for the supplied CourseID Course instructorQuery = schoolContext.Course.Where( "it.CourseID = @courseID", new ObjectParameter ("courseID", courseId)).Include("Person"). FirstOrDefault(); // Display each instructor for the specified course. foreach (Instructor instructor in instructorQuery.Person. OfType<Instructor>()) { studentList.Items.Add("Instructor: " + instructor. LastName + ", " + instructor.FirstName); }
アプリケーションが完成しました。Ctrl キーを押しながら F5 キーを押してアプリケーションを実行します。[View Enrollment] ボタンをクリックして、Enrollment フォームを読み込みます。選択したコースのコース登録者とインストラクタが ListBox に表示されます。
コード リスト
このセクションでは、Enrollment フォームの分離コード ファイルの最終バージョンを示します。
Imports System.Data.Objects
Imports System.Data.Objects.DataClasses
Public Class Enrollment
' Create an ObjectContext instance based on SchoolEntity.
Private schoolContext As SchoolEntities
Private Sub Enrollment_Load(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles MyBase.Load
' Initialize the ObjectContext.
schoolContext = New SchoolEntities()
' Define a query that returns all Department objects and
' related Course objects, ordered by name.
Dim departmentQuery As ObjectQuery(Of Department) = _
schoolContext.Department.Include("Course") _
.OrderBy("it.Name")
' Bind the ComboBox control to the query, which is
' executed during data binding.
Me.departmentList.DataSource = departmentQuery _
.Execute(MergeOption.OverwriteChanges)
Me.departmentList.DisplayMember = "Name"
End Sub
Private Sub departmentList_SelectedIndexChanged(ByVal sender _
As System.Object, ByVal e As System.EventArgs) Handles _
departmentList.SelectedIndexChanged
' Get the object for the selected department
Dim department As Department = CType(Me.departmentList _
.SelectedItem, Department)
' Bind the ComboBox control Course properties of
' the selected Department.
courseList.DataSource = department.Course
courseList.DisplayMember = "Title"
End Sub
Private Sub courseList_SelectedIndexChanged(ByVal sender As _
System.Object, ByVal e As System.EventArgs) Handles _
courseList.SelectedIndexChanged
Me.studentList.Items.Clear()
' Get the CourseID from the selected Course.
Dim course As Course = CType(Me.courseList.SelectedItem, _
Course)
Dim courseId As Integer = course.CourseID
' Get all CourseGrade objects for the supplied CourseID
Dim studentQuery As Course = schoolContext.Course.Where _
("it.CourseID = @courseID", New ObjectParameter _
("courseID", courseId)).Include("CourseGrade"). _
FirstOrDefault()
' Get all students for each CourseGrade
For Each cg As CourseGrade In studentQuery.CourseGrade
cg.PersonReference.Load()
studentList.Items.Add(cg.Person.LastName + ", " + _
cg.Person.FirstName)
Next
studentList.Items.Add(" ")
' Get all Instructor types for the supplied CourseID
Dim instructorQuery As Course = schoolContext.Course. _
Where("it.CourseID = @courseID", New ObjectParameter _
("courseID", courseId)).Include("Person") _
.FirstOrDefault()
' Display each instructor for the specified Course
For Each Instructor As Instructor In instructorQuery.Person
Me.studentList.Items.Add("Instructor: " + Instructor. _
LastName + ", " + Instructor.FirstName)
Next
End Sub
End Class
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Data.Objects;
using System.Data.Objects.DataClasses;
namespace CourseManager
{
public partial class Enrollment : Form
{
// Create an ObjectContext instance based on SchoolEntity.
private SchoolEntities schoolContext;
public Enrollment()
{
InitializeComponent();
}
private void Enrollment_Load(object sender, EventArgs e)
{
// Initialize the ObjectContext.
schoolContext = new SchoolEntities();
// Define a query that returns all Department objects
// and related Course objects, ordered by name.
ObjectQuery<Department> departmentQuery =
schoolContext.Department.Include("Course")
.OrderBy("it.Name");
// Bind the ComboBox control to the query, which is
// executed during data binding.
this.departmentList.DataSource = departmentQuery
.Execute(MergeOption.OverwriteChanges);
this.departmentList.DisplayMember = "Name";
}
private void departmentList_SelectedIndexChanged(object sender,
EventArgs e)
{
// Get the object for the selected department.
Department department =
(Department)this.departmentList.SelectedItem;
// Bind the ComboBox control Course properties of
// the selected Department.
courseList.DataSource = department.Course;
courseList.DisplayMember = "Title";
}
private void courseList_SelectedIndexChanged(object sender,
EventArgs e)
{
studentList.Items.Clear();
// Get the CourseID from the selected Course.
Course course = (Course)courseList.SelectedItem;
int courseId = course.CourseID;
// Get all CourseGrade types for the supplied CourseID
Course studentQuery = schoolContext.Course.Where(
"it.CourseID = @courseID", new ObjectParameter
("courseID", courseId)).Include("CourseGrade").
FirstOrDefault();
// Get all students for each CourseGrade
foreach (CourseGrade cg in studentQuery.CourseGrade)
{
cg.PersonReference.Load();
studentList.Items.Add(cg.Person.LastName + ", " +
cg.Person.FirstName);
}
studentList.Items.Add(" ");
// Get all Instructor types for the supplied CourseID
Course instructorQuery = schoolContext.Course.Where(
"it.CourseID = @courseID", new ObjectParameter
("courseID", courseId)).Include("Person").
FirstOrDefault();
// Display each instructor for the specified course.
foreach (Instructor instructor in instructorQuery.Person.
OfType<Instructor>())
{
studentList.Items.Add("Instructor: " + instructor.
LastName + ", " + instructor.FirstName);
}
}
}
}
次の手順
EDM に Table-Per-Hierarchy 継承を正常に実装できました。Table-Per-Hierarchy 継承を持つ EDM を定義する方法の詳細については、「Table-Per-Hierarchy 継承でモデルを定義する方法 (Entity Framework)」を参照してください。Entity Framework を使用するアプリケーションを構築する方法の詳細については、「プログラミング ガイド (Entity Framework)」を参照してください。
参照
その他のリソース
ADO.NET Entity Data Model デザイナのシナリオ
Entity Data Model ツールのタスク