チュートリアル: Table-Per-Hierarchy 継承のマッピング (Entity Data Model ツール)
このトピックでは、.edmx ファイルで定義された概念モデルを変更することによって、Table-Per-Hierarchy 継承を実装する方法について説明します。 Table-Per-Hierarchy 継承では、1 つのデータベース テーブルを使用して、継承階層のすべてのエンティティ型のデータを維持します。
このチュートリアルでは、CourseManager アプリケーションで使用する概念モデルを変更することによって、Table-Per-Hierarchy 継承を実装します (詳細は、このトピックの「前提条件」を参照してください)。
CourseManager の概念モデルの 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 継承を実装します。 その後、選択したコースの登録を表示できるようにアプリケーションの機能を拡張します。
注 : |
---|
このドキュメントのチュートリアルのトピックの多くは CourseManager アプリケーションを開始点としているため、元の CourseManager コードを編集するのではなく、このチュートリアル用に CourseManager アプリケーションのコピーを使用することをお勧めします。 |
このチュートリアルでは、Visual Studio、.NET Framework、および Visual C# または Visual Basic のプログラミングの基本的なスキルがある読者を想定しています。
Table-Per-Hierarchy 継承の実装
この手順では、SchoolModel を変更することによって 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 に設定します。
通常、Table-Per-Hierarchy のシナリオをモデル化するために抽象型を使用する必要はありません。 ここでは、概念モデルでの使用例を示すために抽象型を使用しています。
注 : この手順の以降の手順では、[マッピングの詳細] ウィンドウを使用します。このウィンドウが表示されていない場合は、デザイン画面を右クリックして [マッピングの詳細] をクリックします。 [マッピングの詳細] ウィンドウで、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 継承が実装されました。
Instructor と OfficeAssignment の間のアソシエーションの作成
このセクションでは、Person と OfficeAssignment の間のアソシエーションを、Instructor と OfficeAssignment との間のアソシエーションに変更します。 このような方法によるアソシエーションの変更は、モデルの目的と整合しています。
Instructor と OfficeAssignment の間のアソシエーションを作成するには
Person と OfficeAssignment の間のアソシエーションを右クリックし、[削除] を選択します。
エンティティ デザイナーのデザイン画面の空の領域を右クリックし、[追加] をポイントして [アソシエーション] を選択します。
アソシエーションの 1 つの [End] について、[エンティティ] ドロップダウン リストから [Instructor] を選択し、[複数要素の接続性] ドロップダウン リストから [1 (1)] を選択します。
アソシエーションのもう 1 つの [End] について、[エンティティ] ドロップダウン リストから [OfficeAssignment] を選択し、[複数要素の接続性] ドロップダウン リストから [0..1 (0 または 1)] を選択します。
[OK] をクリックします。
Instructor と OfficeAssignment の間の新しいアソシエーションを右クリックし、[プロパティ] を選択します。
[プロパティ] ウィンドウで、[参照に関する制約] をクリックしてから、値列に表示される省略記号 [(…)] をクリックします。
[参照に関する制約] ダイアログ ボックスが表示されます。
[プリンシパル] ドロップダウン リストから [Instructor] を選択します。
[OK] をクリックします。
Instructor と OfficeAssignment の間に新しいアソシエーションが追加されました。
ユーザー インターフェイスの構築
次に、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;
ユーザー インターフェイスが完成しました。
概念モデルに対するクエリ
この手順では、概念モデルに対してクエリを実行し、結果を Windows フォーム コントロールにバインドします。 オブジェクトをコントロールにバインドする方法の詳細については、「Binding Objects to Controls (Entity Framework)」を参照してください。
概念モデルに対してクエリを実行するには
フォーム デザイナーで Enrollment フォームが開いた状態で、Enrollment フォームをダブルクリックします。
Enrollment フォームの分離コード ファイルが開きます。
次の using (C#) ステートメントまたは Imports (Visual Basic) ステートメントを追加します。
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 SchoolEntities. 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.Departments.Include("Courses") _ .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.Departments.Include("Courses") .OrderBy("it.Name"); // Bind the ComboBox control to the query. this.departmentList.DataSource = departmentQuery .Execute(MergeOption.OverwriteChanges); this.departmentList.DisplayMember = "Name";
Enrollment フォームのデザイン ビューに戻り、departmentList ComboBox コントロールをダブルクリックします。
departmentList_SelectedIndexChanged イベント ハンドラーが分離コード ファイルで作成されます。
イベント ハンドラーに、選択した Department の Course プロパティに courseList ComboBox をバインドするコードを追加します。
' 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.Courses courseList.DisplayMember = "Title"
// Get the object for the selected department. Department department = (Department)this.departmentList.SelectedItem; // Bind the ComboBox control to the Course // properties of the selected Department. courseList.DataSource = department.Courses; courseList.DisplayMember = "Title";
Enrollment フォームのデザイン ビューに戻り、courseList ComboBox コントロールをダブルクリックします。
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 StudentGrade objects for the supplied CourseID Dim studentQuery As Course = schoolContext.Courses.Where _ ("it.CourseID = @courseID", New ObjectParameter _ ("courseID", courseId)).Include("StudentGrades"). _ FirstOrDefault() ' Get all students for each StudentGrade For Each cg As StudentGrade In studentQuery.StudentGrades 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.Courses. _ Where("it.CourseID = @courseID", New ObjectParameter _ ("courseID", courseId)).Include("People") _ .FirstOrDefault() ' Display each instructor for the specified Course For Each Instructor As Instructor In instructorQuery.People 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 StudentGrade types for the supplied CourseID Course studentQuery = schoolContext.Courses.Where( "it.CourseID = @courseID", new ObjectParameter ("courseID", courseId)).Include("StudentGrades"). FirstOrDefault(); // Get all students for each StudentGrade foreach (StudentGrade sg in studentQuery.StudentGrades) { sg.PersonReference.Load(); studentList.Items.Add(sg.Person.LastName + ", " + sg.Person.FirstName); } studentList.Items.Add(" "); // Get all Instructor types for the supplied CourseID Course instructorQuery = schoolContext.Courses.Where( "it.CourseID = @courseID", new ObjectParameter ("courseID", courseId)).Include("People"). FirstOrDefault(); // Display each instructor for the specified course. foreach (Instructor instructor in instructorQuery.People. 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.Departments.Include("Courses") _
.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.Courses
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 StudentGrade objects for the supplied CourseID
Dim studentQuery As Course = schoolContext.Courses.Where _
("it.CourseID = @courseID", New ObjectParameter _
("courseID", courseId)).Include("StudentGrades"). _
FirstOrDefault()
' Get all students for each StudentGrade
For Each cg As StudentGrade In studentQuery.StudentGrades
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.Courses. _
Where("it.CourseID = @courseID", New ObjectParameter _
("courseID", courseId)).Include("People") _
.FirstOrDefault()
' Display each instructor for the specified Course
For Each Instructor As Instructor In instructorQuery.People
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 SchoolEntities.
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.Departments.Include("Courses")
.OrderBy("it.Name");
// Bind the ComboBox control to the query.
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 to the Course
// properties of the selected Department.
courseList.DataSource = department.Courses;
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 StudentGrade types for the supplied CourseID
Course studentQuery = schoolContext.Courses.Where(
"it.CourseID = @courseID", new ObjectParameter
("courseID", courseId)).Include("StudentGrades").
FirstOrDefault();
// Get all students for each StudentGrade
foreach (StudentGrade sg in studentQuery.StudentGrades)
{
sg.PersonReference.Load();
studentList.Items.Add(sg.Person.LastName + ", " +
sg.Person.FirstName);
}
studentList.Items.Add(" ");
// Get all Instructor types for the supplied CourseID
Course instructorQuery = schoolContext.Courses.Where(
"it.CourseID = @courseID", new ObjectParameter
("courseID", courseId)).Include("People").
FirstOrDefault();
// Display each instructor for the specified course.
foreach (Instructor instructor in instructorQuery.People.
OfType<Instructor>())
{
studentList.Items.Add("Instructor: " + instructor.
LastName + ", " + instructor.FirstName);
}
}
}
}
次の手順
概念モデルに Table-Per-Hierarchy 継承を正常に実装できました。 Table-Per-Hierarchy 継承を持つ概念モデルを定義する方法の詳細については、「How to: Define a Model with Table-per-Hierarchy Inheritance」を参照してください。 Entity Framework を使用するアプリケーションを構築する方法の詳細については、「ADO.NET Entity Framework」を参照してください。