デザイナーの TPH 継承

このステップバイステップのチュートリアルでは、Entity Framework Designer (EF Designer) を使用して、概念モデルに Table-Per-Hierarchy (TPH) 継承を実装する方法について説明します。 TPH 継承では、1 つのデータベース テーブルを使用して、継承階層のすべてのエンティティ型のデータを管理します。

このチュートリアルでは、Person テーブルを、Person (基本データ型)、Student (Person から派生)、Instructor (Person から派生) の 3 つのエンティティ型にマップします。 データベースから概念モデルを作成し (Database First)、EF Designer を使用して、TPH 継承を実装するようにモデルを変更します。

Model First を使用して TPH 継承にマップすることもできますが、複雑な独自のデータベース生成ワークフローを作成する必要があります。 その後、EF Designer でこのワークフローを [データベース生成ワークフロー] プロパティに割り当てます。 別の方法として、Code First を使用する方が簡単です。

他の継承オプション

Table-Per-Type (TPT) は、データベースの個々のテーブルが、継承に参加しているエンティティにマップされる別の種類の継承です。  EF Designer を使用して Table-Per-Type 継承をマップする方法については、EF Designer の TPT 継承に関する記事を参照してください。

Table-Per-Concrete type (TPC) 継承モデルと混合継承モデルは、Entity Framework ランタイムではサポートされていますが、EF Designer ではサポートされていません。 TPC または混合継承を使用する場合、Code First の使用または EDMX ファイルの手動編集の 2 つの方法があります。 EDMX ファイルを使用する場合、[マッピングの詳細] ウィンドウは "セーフ モード" になり、デザイナーを使用してマッピングを変更することはできなくなります。

前提条件

このチュートリアルを完了するための要件を次に示します。

プロジェクトをセットアップする

  • Visual Studio 2012 を開きます。
  • [ファイル] -> [新規作成] -> [プロジェクト] の順に選択します。
  • 左ペインで [Visual C#] をクリックし、[コンソール] テンプレートを選択します。
  • 名前として「TPHDBFirstSample」と入力します。
  • [OK] を選択します。

Create a Model (モデルの作成)

  • ソリューション エクスプローラーでプロジェクト名を右クリックし、[追加] -> [新しい項目] の順に選択します。
  • 左側のメニューから [データ] を選択し、[テンプレート] ペインの [ADO.NET Entity Data Model] を選択します。
  • ファイル名として「TPHModel.edmx」と入力し、[追加] をクリックします。
  • [モデルのコンテンツの選択] ダイアログ ボックスで、[データベースから生成] を選択し、[次へ] をクリックします。
  • [新しい接続] をクリックします。 [接続のプロパティ] ダイアログ ボックスで、サーバー名 (例: (localdb)\mssqllocaldb) を入力し、認証方法を選択します。データベース名として「School」と入力し、[OK] をクリックします。 指定したデータベース接続設定に従って、[データ接続の選択] ダイアログ ボックスが更新されます。
  • [データベース オブジェクトの選択] ダイアログ ボックスで、[テーブル] ノードの Person テーブルを選択します。
  • [完了] をクリックします。

モデルを編集するためのデザイン サーフェイスを提供する Entity Designer が表示されます。 [データベース オブジェクトの選択] ダイアログ ボックスで選択したすべてのオブジェクトがモデルに追加されます。

データベースの Person テーブルは次のようになります。

Person Table 

Table-Per-Hierarchy 継承を実装する

Person テーブルには Discriminator 列があり、"Student" と "Instructor" の 2 つの値のいずれかを含めることができます。 値に応じて、Person テーブルは Student エンティティまたは Instructor エンティティにマップされます。 Person テーブルには、HireDateEnrollmentDate の 2 つの列もあります。(少なくともこのチュートリアルでは) 個人を同時に学生と講師にすることはできないため、これらの列は Null 許容にする必要があります。

新しいエンティティを追加する

  • 新しいエンティティを追加します。 これを行うには、Entity Framework Designer のデザイン サーフェイスの空の領域を右クリックし、[追加] -> [エンティティ] の順に選択します。
  • [エンティティ名] に「Instructor」と入力し、[基本データ型] のドロップダウン リストから [Person] を選択します。
  • [OK] をクリックします。
  • 新しいエンティティをもう 1 つ追加します。 [エンティティ名] に「Student」と入力し、[基本データ型] のドロップダウン リストから [Person] を選択します。

デザイン サーフェイスに 2 つの新しいエンティティ型が追加されました。 新しいエンティティ型から Person エンティティ型に向かう矢印は、その Person が新しいエンティティ型の基本データ型であることを示しています。

  • Person エンティティの HireDate プロパティを右クリックします。 [切り取り] を選択します (または Ctrl + X キーを使います)。
  • Instructor エンティティを右クリックし、[貼り付け] を選択します (または Ctrl + V キーを使います)。
  • HireDate プロパティを右クリックし、[プロパティ] を選択します。
  • [プロパティ] ウィンドウで、[Null 許容] プロパティを false に設定します。
  • Person エンティティの EnrollmentDate プロパティを右クリックします。 [切り取り] を選択します (または Ctrl + X キーを使います)。
  • Student エンティティを右クリックし、[貼り付け] を選択します (または Ctrl + V キーを使います)。
  • EnrollmentDate プロパティを選択し、[Null 許容] プロパティを false に設定します。
  • Person エンティティ型を選択します。 [プロパティ] ウィンドウで、[抽象] プロパティを true に設定します。
  • Person から [識別子] プロパティを削除します。 削除する理由については、次のセクションで説明します。

エンティティをマップする

  • Instructor を右クリックし、[テーブル マッピング] を選択します。[マッピングの詳細] ウィンドウで、Instructor エンティティが選択されます。

  • [マッピングの詳細] ウィンドウで、[<テーブルまたはビューの追加>] をクリックします。 [<テーブルまたはビューの追加>] フィールドは、選択したエンティティをマップできるテーブルまたはビューを示すドロップダウン リストになります。

  • ドロップダウン リストから [Person] を選択します。

  • [マッピングの詳細] ウィンドウが更新され、既定の列マッピング、および条件を追加するオプションが表示されます。

  • [<条件の追加>] をクリックします。 [<条件の追加>] フィールドは、条件を設定できる列を示すドロップダウン リストになります。

  • ドロップダウン リストから [Discriminator] を選択します。

  • [マッピングの詳細] ウィンドウの [演算子] 列で、ドロップダウン リストから [=] を選択します。

  • [値/プロパティ] 列に「Instructor」と入力します。 最終的な結果はこのようになります。

    Mapping Details

  • Student エンティティ型に対してこれらの手順を繰り返します。ただし、条件には Student 値を指定します。
    [識別子] プロパティを削除したのは、テーブル列を複数回マップすることはできないためです。 この列は条件付きマッピングに使用されるため、プロパティ マッピングには使用できません。 両方に使用できるのは、条件で Is Null または Is Not Null 比較を使用している場合だけです。

これで、Table-Per-Hierarchy 継承が実装されました。

Final TPH

モデルを使用する

Main メソッドが定義されている Program.cs ファイルを開きます。 Main 関数に次のコードを貼り付けます。 このコードでは、3 つのクエリが実行されます。 最初のクエリでは、すべての Person オブジェクトが返されます。 2 番目のクエリでは、OfType メソッドを使用して、Instructor オブジェクトが返されます。 3 番目のクエリでは、OfType メソッドを使用して、Student オブジェクトが返されます。

    using (var context = new SchoolEntities())
    {
        Console.WriteLine("All people:");
        foreach (var person in context.People)
        {
            Console.WriteLine("    {0} {1}", person.FirstName, person.LastName);
        }

        Console.WriteLine("Instructors only: ");
        foreach (var person in context.People.OfType<Instructor>())
        {
            Console.WriteLine("    {0} {1}", person.FirstName, person.LastName);
        }

        Console.WriteLine("Students only: ");
        foreach (var person in context.People.OfType<Student>())
        {
            Console.WriteLine("    {0} {1}", person.FirstName, person.LastName);
        }
    }