継承 (EDM)
エンティティ データ モデル (EDM) では、継承を使用することによって、別の型の機能を拡張した派生型を作成できます。
通常、アプリケーションによって使用されるモデルには、さまざまな型が含まれています。その中には、ビジネス アプリケーションにおける customer と order など、別個の概念がモデル化されたエンティティ型も存在します。これらのデータ型はメンバを一切共有していません。しかし、型によっては互いに類似性の高いものもあります。たとえば、customer 型と employee 型について考えてみましょう。これらの型では異なる概念がモデル化されますが、根本的な共通性が存在します。どちらも、取引関係にかかわる人を表しており、また、名前、住所、電話番号などの情報を格納するプロパティを持っています。
EDM の継承では、ある型を別の型から派生させることができます。たとえば、Employee
と Customer
は、どちらも Contact
型から派生させることが可能です。この場合、Contact
を基本データ型と呼び、Employee
と Customer
を派生型と呼びます。
継承による派生は 1 レベルだけに限定されておらず、派生型を別のエンティティ型の基本データ型とすることもできます。たとえば、Employee
を Manager
の基本データ型として使用したり、Customer
を PreferredCustomer
の基本データ型として使用したりすることが可能です。このように継承を重ねることによって階層が構築されていきます。
共通言語ランタイム (CLR) と同様、EDM システムではデータ型の単一継承のみサポートされます。エンティティ型は、1 つのスーパータイプからのみ、プロパティを直接継承できます。
[!メモ]
EDM システムはメソッドを実装しないため、メソッドの継承はサポートされません。EDM では、ヘルパ メソッドを部分クラスで実装します。詳細については、「ヘルパ メソッド (EDM)」を参照してください。
継承の例
次の例は、先ほど説明した継承階層の概念スキーマ定義言語 (CSDL) の仕様を抜粋したものです。
<EntityType Name="Contact">
<Key>
<PropertyRef Name="ContactId" />
</Key>
<Property Name="ContactId" Type="Int32" Nullable="false" />
<Property Name="Name" Type="String" />
<Property Name="Address" Type="Address" />
<Property Name="Phone" Type="String" />
</EntityType>
<EntityType Name="Customer" BaseType="Contact">
<Property Name="CustomerID" Type="String" />
<Property Name="CompanyName" Type="String" />
</EntityType>
<EntityType Name="PreferredCustomer" BaseType="Customer">
<Property Name="PreferenceCode" Type="String" />
<Property Name="CreditLimit" Type="Decimal" />
</EntityType>
<!-- Similar Declarations for Employee and Manager -->
継承の目的の 1 つは、複数の型で基本的な構造を共有することです。継承を使用するもう 1 つの理由として、拡張性があります。既にアプリケーション内で継承階層が実装され、展開されていたとしても、開発者は継承を通じて型を拡張させることができます。
オブジェクト指向プログラミングの概念である多態性や値の置換可能性は、継承に依存しています。派生されたデータ型のすべてのインスタンスは、その基本データ型のインスタンスでもあります。たとえば、Manager
が Employee
から派生されている場合、Manager
のすべてのインスタンスは、Employee
のインスタンスでもある、ということです。すべての従業員 (基本データ型) を照会した場合、マネージャ (派生型) もすべて返されることになります。
継承の詳細については、「Table-per-Type 継承でモデルを定義する方法」および「Table-Per-Hierarchy 継承でモデルを定義する方法 (Entity Framework)」を参照してください。
継承の実装
CSDL で定義される継承階層は、EDM モデルで構築されたアプリケーションのデータを格納するデータベース テーブル内で実装されている必要があります。データベース テーブル内で継承階層として型を構築するには、いくつかの方法があります。
どの実装も、基本データ型と派生型とで、キーの構造が一致している必要があります。つまり、Customer
型を Person
型から派生する場合、Customer
型と Person
型に、同じキー プロパティが指定されている必要があります。
複数のテーブルを使った継承の実装では、複数のテーブルで、キー プロパティに同じ値を割り当てることによって、派生型が先祖の型のインスタンスとして識別されます。
マッピング スキーマでは、継承の実装に EntityTypeMapping の IsTypeOf<T> インジケータを使用することによって、派生型と基本データ型の関係 (Customer
型が Person という基本データ型でもあるなど) を指定できます。また、概念スキーマとストレージ スキーマでは、どちらも EntityType の BaseType 属性を使用して、派生エンティティの基本データ型を指定できます。
Table-per-Hierarchy モデル
Table-Per-Hierarchy モデルに基づく継承では、同じデータベース テーブルを使用することによって、複数の型が階層として指定されます。このモデルでは、マッピング スキーマ内の明示的な条件によって、継承階層における型が識別されます。
テーブルにはそれぞれの型を識別するための識別子列が存在し、各型の列に割り当てられた値が条件によって参照されます。たとえば、EntityType
列の値が P
(Person 型の場合) または C
(Customer 型の場合) と等しいなどの条件が考えられます。
次の例は、Customer
型と Person
型の条件のマッピングを示しています。
<?xml version="1.0" encoding="utf-8"?>
<Mapping Space="C-S"
xmlns:cdm="urn:schemas-microsoft-com:windows:storage:mapping:CS"
xmlns="urn:schemas-microsoft-com:windows:storage:mapping:CS">
<EntityContainerMapping>
<EntitySetMapping Name="CCustomer1">
<EntityTypeMapping TypeName="CNorthwind.CCustomer">
<MappingFragment StoreEntitySet="SParty1">
<EntityKey>
<ScalarProperty Name="PartyID" ColumnName="PartyID" />
</EntityKey>
<ScalarProperty Name="PartyName" ColumnName="PartyName" />
<ScalarProperty Name="PartyTitle" ColumnName="Title" />
<ScalarProperty Name="ContactName" ColumnName="ContactName" />
<Condition Value="C" ColumnName="EntityType" />
</MappingFragment>
</EntityTypeMapping>
</EntitySetMapping>
<EntitySetMapping Name="CSupplier1">
<EntityTypeMapping TypeName="CNorthwind.CSupplier">
<MappingFragment StoreEntitySet="SParty1">
<EntityKey>
<ScalarProperty Name="PartyID" ColumnName="PartyID" />
</EntityKey>
<ScalarProperty Name="PartyName" ColumnName="PartyName" />
<ScalarProperty Name="PartyTitle" ColumnName="Title" />
<ScalarProperty Name="HomePage" ColumnName="HomePage" />
<Condition Value="S" ColumnName="EntityType" />
</MappingFragment>
</EntityTypeMapping>
</EntitySetMapping>
</EntityContainerMapping>
</Mapping>
Table-per-Type モデル
Table-Per-Type モデルに基づく継承では、基本データ型 (または先祖型) と派生型とが、別々のテーブルに格納されます。派生型に固有のフィールドは、派生型を表すテーブルにのみ存在します。この継承モデルにおけるエンティティのマッピングは、複数の物理テーブルにまたがります。階層内の型は、テーブルに行が存在するかどうかで識別されます。すべてのテーブルのキー プロパティには同じキー値が割り当てられます。
概念スキーマおよびストレージ スキーマでは、EntityType の BaseType 属性を使用して、基本データ型からの継承を指定できます。マッピング スキーマでは、基本データ型の指定に IsTypeOf<T> インジケータを使用して、マッピング構文の重複を排除できます。
次の例は、Customer
型と Person
型に対する 2 つの別個のマッピングを示しています。Person
型と Customer
型は、EntityTypeMapping 要素の TypeName 属性によって区別されます。それぞれの型について、テーブルが存在します。Customer
のエンティティ型とテーブルには、基本データ型にはない、NumYears
というプロパティと列が存在します。
<?xml version="1.0" encoding="utf-8"?>
<Mapping Space="C-S"
xmlns:cdm="urn:schemas-microsoft-com:windows:storage:mapping:CS"
xmlns="urn:schemas-microsoft-com:windows:storage:mapping:CS">
<EntityContainerMapping EntityContainerName="CNorthwind.LOBData">
<EntitySetMapping Name="Person1">
<EntityTypeMapping TypeName="CNorthwind.Person">
<MappingFragment StoreEntitySet="Person">
<ScalarProperty Name="PersonID" ColumnName="PersonID" />
<ScalarProperty Name="Name" ColumnName="Name" />
</MappingFragment>
</EntityTypeMapping>
<EntityTypeMapping TypeName="CNorthwind.Customer">
<MappingFragment StoreEntitySet="Customer">
<ScalarProperty Name="PersonID" ColumnName="PersonID" />
<ScalarProperty Name="Name" ColumnName="Name" />
<ScalarProperty Name="NumYears" ColumnName="NumYears" />
</MappingFragment>
</EntityTypeMapping>
</EntitySetMapping>
</EntityContainerMapping>
</Mapping>
参照
処理手順
Table-Per-Type 継承でモデルを定義する方法 (Entity Framework)
Table-Per-Hierarchy 継承でモデルを定義する方法 (Entity Framework)
概念
Entity Data Model の型
ヘルパ メソッド (EDM)
Entity Data Model の継承 (アプリケーション シナリオ)