How to: Define a Model with Table-per-Type Inheritance (Entity Framework)
Inheritance can be implemented in several ways in the Entity Data Model (EDM). The table-per-type method uses a separate table in storage to maintain data for each type in the inheritance hierarchy. This section includes the schemas and mapping for a simple inheritance hierarchy implemented on the table-per-type scenario.
In the EDM, the conceptual schema for an inheritance hierarchy on the table-per-type model includes the BaseType attribute assignment for declarations of derived types. Each derived EntityType is declared separately, but the declaration of the EntityContainer only includes an EntitySet declaration for the base type.
Associations in this scenario are implemented on the base type because the definitions of associations refer to EntitySet declarations. A derived type has no EntitySet declaration in the EntityContainer.
Create a class library project.
Click Add New Item, and add an ADO.NET Entity Data Model.
When the wizard appears, create an empty model.
Open the .edmx file with an XML editor and add find the conceptual schema definition language (CSDL) segment of the file.
Implement the CSDL schema. This schema includes declarations in a namespace called SchoolDataLib. The inheritance hierarchy includes an EntityType named Department, which is the base type, and three derived entities for department of business, department of engineering, and department of music. Only the base type, Department, has a Key attribute assignment. The derived types DeptBusiness, DeptEngineering, and DeptMusic include the BaseType attribute assignment. The Key columns of the tables that represent the derived types in storage are all mapped to the Key column of the table that represents the base type.
Implement an AssociationType between the Department and Person entities. This association is used in the implementation of the navigation property for a School Administrator. The FK_Department_Administrator defines an association used by all the types derived from the Department type on which it is declared. The NavigationProperty using this AssociationType is inherited by all the derived types. The complete CSDL schema is shown below.
<!-- CSDL content -->
<Schema
xmlns="https://schemas.microsoft.com/ado/2006/04/edm"
Namespace="SchoolDataLib"
Alias="Self">
<EntityType Name="Department">
<!--Base type table-per-type inheritance-->
<Key>
<PropertyRef Name="DepartmentID" />
</Key>
<Property Name="DepartmentID" Type="Int32" Nullable="false" />
<Property Name="Name" Type="String" Nullable="false" />
<Property Name="Budget" Type="Decimal" Nullable="false" />
<Property Name="StartDate" Type="DateTime" Nullable="false" />
<NavigationProperty Name="Administrator"
Relationship="SchoolDataLib.FK_Department_Administrator"
FromRole="Department" ToRole="Person" />
</EntityType>
<EntityType Name="DeptBusiness" BaseType="SchoolDataLib.Department">
<Property Name="LegalBudget" Type="Decimal" Nullable="false" />
<Property Name="AccountingBudget" Type="Decimal" Nullable="false" />
</EntityType>
<EntityType Name="DeptEngineering" BaseType="SchoolDataLib.Department">
<Property Name="FiberOpticsBudget" Type="Decimal" Nullable="false" />
<Property Name="LabBudget" Type="Decimal" Nullable="false" />
</EntityType>
<EntityType Name="DeptMusic" BaseType="SchoolDataLib.Department">
<Property Name="TheaterBudget" Type="Decimal" Nullable="false" />
<Property Name="InstrumentBudget" Type="Decimal" Nullable="false" />
</EntityType>
<Association Name="FK_Department_Administrator">
<End Role="Person" Type="SchoolDataLib.Person" Multiplicity="0..1" />
<End Role="Department" Type="SchoolDataLib.Department" Multiplicity="*" />
</Association>
<EntityType Name="Person">
<!--Base type table-per-hierarchy inheritance-->
<Key>
<PropertyRef Name="PersonID" />
</Key>
<Property Name="PersonID" Type="Int32" Nullable="false" />
<Property Name="FirstName" Type="String" Nullable="false" />
<Property Name="LastName" Type="String" Nullable="false" />
<NavigationProperty Name="Department"
Relationship="SchoolDataLib.FK_Department_Administrator"
FromRole="Person" ToRole="Department" />
</EntityType>
<EntityType Name="Student" BaseType="SchoolDataLib.Person">
<Property Name="EnrollmentDate" Type="DateTime" />
</EntityType>
<EntityType Name="Instructor" BaseType="SchoolDataLib.Person">
<Property Name="HireDate" Type="DateTime" />
</EntityType>
<EntityType Name="Administrator" BaseType="SchoolDataLib.Person">
<Property Name="AdminDate" Type="DateTime" />
</EntityType>
<EntityContainer Name="SchoolDataLibContainer">
<EntitySet Name="Departments" EntityType="SchoolDataLib.Department" />
<EntitySet Name="People" EntityType="SchoolDataLib.Person" />
<AssociationSet Name="FK_Department_Administrator"
Association="SchoolDataLib.FK_Department_Administrator">
<End Role="Person" EntitySet="People" />
<End Role="Department" EntitySet="Departments" />
</AssociationSet>
</EntityContainer>
</Schema>
Define the tables that contain data for each type in the inheritance hierarchy in the store schema definition language (SSDL) segment of the .edmx file. Unlike the entity declarations in the conceptual schema, entities for the derived types in the storage model have a Key property.
Use the data types of the database management system instead of the common language runtime (CLR) types that are used in the conceptual schema for properties in the storage schema.
Use the following syntax in SSDL to define the complete storage metadata used in this inheritance scenario.
<!-- SSDL content -->
<Schema
xmlns="https://schemas.microsoft.com/ado/2006/04/edm/ssdl"
Namespace="SchoolDataLib.Target"
Provider="System.Data.SqlClient"
ProviderManifestToken="2005"
Alias="Self">
<EntityContainer Name="dbo">
<EntitySet Name="Department" EntityType="SchoolDataLib.Target.Department" />
<EntitySet Name="DeptBusiness" EntityType="SchoolDataLib.Target.DeptBusiness" />
<EntitySet Name="DeptEngineering" EntityType="SchoolDataLib.Target.DeptEngineering" />
<EntitySet Name="DeptMusic" EntityType="SchoolDataLib.Target.DeptMusic" />
<EntitySet Name="Person" EntityType="SchoolDataLib.Target.Person" />
<AssociationSet Name="FK_Department_Administrator"
Association="SchoolDataLib.Target.FK_Department_Administrator">
<End Role="Person" EntitySet="Person" />
<End Role="Department" EntitySet="Department" />
</AssociationSet>
</EntityContainer>
<EntityType Name="Department">
<Key>
<PropertyRef Name="DepartmentID" />
</Key>
<Property Name="DepartmentID" Type="int" Nullable="false" />
<Property Name="Name" Type="nvarchar" Nullable="false" MaxLength="50" />
<Property Name="Budget" Type="money" Nullable="false" />
<Property Name="StartDate" Type="datetime" Nullable="false" />
<Property Name="Administrator" Type="int" />
</EntityType>
<EntityType Name="DeptBusiness">
<Key>
<PropertyRef Name="BusinessDeptID" />
</Key>
<Property Name="BusinessDeptID" Type="int" Nullable="false" />
<Property Name="LegalBudget" Type="money" Nullable="false" />
<Property Name="AccountingBudget" Type="money" Nullable="false" />
</EntityType>
<EntityType Name="DeptEngineering">
<Key>
<PropertyRef Name="EngineeringDeptID" />
</Key>
<Property Name="EngineeringDeptID" Type="int" Nullable="false" />
<Property Name="FiberOpticsBudget" Type="money" Nullable="false" />
<Property Name="LabBudget" Type="money" Nullable="false" />
</EntityType>
<EntityType Name="DeptMusic">
<Key>
<PropertyRef Name="DeptMusicID" />
</Key>
<Property Name="DeptMusicID" Type="int" Nullable="false" />
<Property Name="TheaterBudget" Type="money" Nullable="false" />
<Property Name="InstrumentBudget" Type="money" Nullable="false" />
</EntityType>
<EntityType Name="Person">
<Key>
<PropertyRef Name="PersonID" />
</Key>
<Property Name="PersonID" Type="int" Nullable="false" />
<Property Name="FirstName" Type="nvarchar" Nullable="false" MaxLength="50" />
<Property Name="LastName" Type="nvarchar" Nullable="false" MaxLength="50" />
<Property Name="HireDate" Type="datetime" />
<Property Name="EnrollmentDate" Type="datetime" />
<Property Name="AdminDate" Type="datetime" />
<Property Name="PersonCategory" Type="smallint" Nullable="false" />
</EntityType>
<Association Name="FK_Department_Administrator">
<End Role="Person" Type="SchoolDataLib.Target.Person" Multiplicity="0..1" />
<End Role="Department" Type="SchoolDataLib.Target.Department" Multiplicity="*" />
<ReferentialConstraint>
<Principal Role="Person">
<PropertyRef Name="PersonID" />
</Principal>
<Dependent Role="Department">
<PropertyRef Name="Administrator" />
</Dependent>
</ReferentialConstraint>
</Association>
</Schema>
Use the following script with SQL Server Management Studio to generate the database used in this example and the example How to: Define a Model with Table-per-Hierarchy Inheritance (Entity Framework).
Point to New on the File menu, and then click Database Engine Query.
Type either localhost or the name of another SQL Server instance in the Connect to Database Engine dialog box, and then click Connect.
Paste the following Transact-SQL script in the query window and then click Execute.
Nota
This data is used for both this table-per-type example and for the table-per-hierarchy example in the topic How to: Define a Model with Table-per-Hierarchy Inheritance. Only Department
and its derived types apply to this example.
USE [master]
GO
CREATE DATABASE [SchoolData]
GO
USE [SchoolData]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[DeptBusiness]') AND type in (N'U'))
BEGIN
CREATE TABLE [dbo].[DeptBusiness](
[BusinessDeptID] [int] NOT NULL,
[LegalBudget] [money] NOT NULL,
[AccountingBudget] [money] NOT NULL,
CONSTRAINT [PK_DeptBusiness] PRIMARY KEY CLUSTERED
(
[BusinessDeptID] ASC
)WITH (PAD_INDEX = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
END
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[DeptEngineering]') AND type in (N'U'))
BEGIN
CREATE TABLE [dbo].[DeptEngineering](
[EngineeringDeptID] [int] NOT NULL,
[FiberOpticsBudget] [money] NOT NULL,
[LabBudget] [money] NOT NULL,
CONSTRAINT [PK_DeptEngineering] PRIMARY KEY CLUSTERED
(
[EngineeringDeptID] ASC
)WITH (PAD_INDEX = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
END
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[DeptMusic]') AND type in (N'U'))
BEGIN
CREATE TABLE [dbo].[DeptMusic](
[DeptMusicID] [int] NOT NULL,
[TheaterBudget] [money] NOT NULL,
[InstrumentBudget] [money] NOT NULL,
CONSTRAINT [PK_DeptMusic] PRIMARY KEY CLUSTERED
(
[DeptMusicID] ASC
)WITH (PAD_INDEX = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
END
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[Course]') AND type in (N'U'))
BEGIN
CREATE TABLE [dbo].[Course](
[CourseID] [int] NOT NULL,
[Title] [nvarchar](100) NOT NULL,
[StartDate] [datetime] NOT NULL,
[EndDate] [datetime] NOT NULL,
[Credits] [int] NULL,
CONSTRAINT [PK_Course] PRIMARY KEY CLUSTERED
(
[CourseID] ASC
)WITH (PAD_INDEX = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
END
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[Person]') AND type in (N'U'))
BEGIN
CREATE TABLE [dbo].[Person](
[PersonID] [int] NOT NULL,
[FirstName] [nvarchar](50) NOT NULL,
[LastName] [nvarchar](50) NOT NULL,
[HireDate] [datetime] NULL,
[EnrollmentDate] [datetime] NULL,
[PersonCategory] [smallint] NOT NULL,
[AdminDate] [datetime] NULL,
CONSTRAINT [PK_Person] PRIMARY KEY CLUSTERED
(
[PersonID] ASC
)WITH (PAD_INDEX = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
END
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[Enrollment]') AND type in (N'U'))
BEGIN
CREATE TABLE [dbo].[Enrollment](
[EnrollmentID] [int] NOT NULL,
[CourseID] [int] NOT NULL,
[StudentID] [int] NOT NULL,
CONSTRAINT [PK_Enrollment] PRIMARY KEY CLUSTERED
(
[EnrollmentID] ASC
)WITH (PAD_INDEX = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
END
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[CourseInstructor]') AND type in (N'U'))
BEGIN
CREATE TABLE [dbo].[CourseInstructor](
[CourseInstructorID] [int] NOT NULL,
[CourseID] [int] NOT NULL,
[InstructorID] [int] NOT NULL,
CONSTRAINT [PK_CourseInstructor] PRIMARY KEY CLUSTERED
(
[CourseInstructorID] ASC
)WITH (PAD_INDEX = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
END
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[Department]') AND type in (N'U'))
BEGIN
CREATE TABLE [dbo].[Department](
[DepartmentID] [int] NOT NULL,
[Name] [nvarchar](50) NOT NULL,
[Budget] [money] NOT NULL,
[StartDate] [datetime] NOT NULL,
[Administrator] [int] NULL,
CONSTRAINT [PK_Department] PRIMARY KEY CLUSTERED
(
[DepartmentID] ASC
)WITH (PAD_INDEX = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
END
GO
IF NOT EXISTS (SELECT * FROM sys.foreign_keys WHERE object_id = OBJECT_ID(N'[dbo].[FK_Enrollment_Course]') AND parent_object_id = OBJECT_ID(N'[dbo].[Enrollment]'))
ALTER TABLE [dbo].[Enrollment] WITH CHECK ADD CONSTRAINT [FK_Enrollment_Course] FOREIGN KEY([CourseID])
REFERENCES [dbo].[Course] ([CourseID])
GO
ALTER TABLE [dbo].[Enrollment] CHECK CONSTRAINT [FK_Enrollment_Course]
GO
IF NOT EXISTS (SELECT * FROM sys.foreign_keys WHERE object_id = OBJECT_ID(N'[dbo].[FK_Enrollment_Student]') AND parent_object_id = OBJECT_ID(N'[dbo].[Enrollment]'))
ALTER TABLE [dbo].[Enrollment] WITH CHECK ADD CONSTRAINT [FK_Enrollment_Student] FOREIGN KEY([StudentID])
REFERENCES [dbo].[Person] ([PersonID])
GO
ALTER TABLE [dbo].[Enrollment] CHECK CONSTRAINT [FK_Enrollment_Student]
GO
IF NOT EXISTS (SELECT * FROM sys.foreign_keys WHERE object_id = OBJECT_ID(N'[dbo].[FK_CourseInstructor_Course]') AND parent_object_id = OBJECT_ID(N'[dbo].[CourseInstructor]'))
ALTER TABLE [dbo].[CourseInstructor] WITH CHECK ADD CONSTRAINT [FK_CourseInstructor_Course] FOREIGN KEY([CourseID])
REFERENCES [dbo].[Course] ([CourseID])
GO
ALTER TABLE [dbo].[CourseInstructor] CHECK CONSTRAINT [FK_CourseInstructor_Course]
GO
IF NOT EXISTS (SELECT * FROM sys.foreign_keys WHERE object_id = OBJECT_ID(N'[dbo].[FK_CourseInstructor_Instructor]') AND parent_object_id = OBJECT_ID(N'[dbo].[CourseInstructor]'))
ALTER TABLE [dbo].[CourseInstructor] WITH CHECK ADD CONSTRAINT [FK_CourseInstructor_Instructor] FOREIGN KEY([InstructorID])
REFERENCES [dbo].[Person] ([PersonID])
GO
ALTER TABLE [dbo].[CourseInstructor] CHECK CONSTRAINT [FK_CourseInstructor_Instructor]
GO
IF NOT EXISTS (SELECT * FROM sys.foreign_keys WHERE object_id = OBJECT_ID(N'[dbo].[FK_Department_Administrator]') AND parent_object_id = OBJECT_ID(N'[dbo].[Department]'))
ALTER TABLE [dbo].[Department] WITH CHECK ADD CONSTRAINT [FK_Department_Administrator] FOREIGN KEY([Administrator])
REFERENCES [dbo].[Person] ([PersonID])
GO
ALTER TABLE [dbo].[Department] CHECK CONSTRAINT [FK_Department_Administrator]
Combine the EntityTypeMapping tags for the derived types under the EntitySet mapping for the base type. In the following mapping specification language (MSL) schema, the EntitySet is named Departments as defined in the conceptual schema.
Use EntityTypeMapping tags under the EntitySet mapping for both the base type and the derived types.
Specify each type being mapped under EntityTypeMapping by the TypeName attribute.
Follow a TableName attribute with a TableMappingFragment.
Map properties of entity types to columns specified in the storage metadata by using ScalarProperty tags.
Notice that the identity columns of the derived types are all mapped to the identity property of the base type, in this case named DepartmentID.
Use the following MSL syntax to map an EntitySet by using the base class Department.
Specify an EntityTypeMapping for each of the derived types.
<!-- C-S mapping content -->
<Mapping xmlns="urn:schemas-microsoft-com:windows:storage:mapping:CS"
Space="C-S">
<Alias Key="Model" Value="SchoolDataLib" />
<Alias Key="Target" Value="SchoolDataLib.Target" />
<EntityContainerMapping CdmEntityContainer="SchoolDataLibContainer"
StorageEntityContainer="dbo">
<!-- Mapping for table-per-type inheritance-->
<EntitySetMapping Name="Departments">
<EntityTypeMapping
TypeName="IsTypeOf(SchoolDataLib.Department)">
<MappingFragment StoreEntitySet="Department">
<ScalarProperty
Name="DepartmentID" ColumnName="DepartmentID" />
<ScalarProperty Name="Name" ColumnName="Name" />
<ScalarProperty Name="Budget" ColumnName="Budget" />
<ScalarProperty
Name="StartDate" ColumnName="StartDate" />
</MappingFragment>
</EntityTypeMapping>
<EntityTypeMapping TypeName="SchoolDataLib.DeptBusiness">
<MappingFragment StoreEntitySet="DeptBusiness">
<ScalarProperty Name="DepartmentID"
ColumnName="BusinessDeptID" />
<ScalarProperty Name="AccountingBudget"
ColumnName="AccountingBudget" />
<ScalarProperty Name="LegalBudget"
ColumnName="LegalBudget" />
</MappingFragment>
</EntityTypeMapping>
<EntityTypeMapping TypeName="SchoolDataLib.DeptEngineering">
<MappingFragment StoreEntitySet="DeptEngineering">
<ScalarProperty Name="DepartmentID"
ColumnName="EngineeringDeptID" />
<ScalarProperty Name="FiberOpticsBudget"
ColumnName="FiberOpticsBudget" />
<ScalarProperty Name="LabBudget"
ColumnName="LabBudget" />
</MappingFragment>
</EntityTypeMapping>
<EntityTypeMapping TypeName="SchoolDataLib.DeptMusic">
<MappingFragment StoreEntitySet="DeptMusic">
<ScalarProperty Name="DepartmentID"
ColumnName="DeptMusicID" />
<ScalarProperty Name="TheaterBudget"
ColumnName="TheaterBudget" />
<ScalarProperty Name="InstrumentBudget"
ColumnName="InstrumentBudget" />
</MappingFragment>
</EntityTypeMapping>
</EntitySetMapping>
<!--Mapping for table-per-hierarchy inheritance-->
<EntitySetMapping Name="People">
<EntityTypeMapping TypeName="SchoolDataLib.Person">
<MappingFragment StoreEntitySet="Person">
<ScalarProperty Name="PersonID" ColumnName="PersonID"/>
<ScalarProperty Name="FirstName" ColumnName="FirstName"/>
<ScalarProperty Name="LastName" ColumnName="LastName"/>
<Condition ColumnName="PersonCategory" Value="0" />
</MappingFragment>
</EntityTypeMapping>
<EntityTypeMapping TypeName="SchoolDataLib.Student">
<MappingFragment StoreEntitySet="Person">
<ScalarProperty Name="PersonID" ColumnName="PersonID" />
<ScalarProperty Name="FirstName" ColumnName="FirstName" />
<ScalarProperty Name="LastName" ColumnName="LastName" />
<ScalarProperty
Name="EnrollmentDate" ColumnName="EnrollmentDate" />
<Condition ColumnName="PersonCategory" Value="1" />
</MappingFragment>
</EntityTypeMapping>
<EntityTypeMapping TypeName="SchoolDataLib.Instructor">
<MappingFragment StoreEntitySet="Person">
<ScalarProperty Name="PersonID" ColumnName="PersonID" />
<ScalarProperty Name="FirstName" ColumnName="FirstName" />
<ScalarProperty Name="LastName" ColumnName="LastName" />
<ScalarProperty Name="HireDate" ColumnName="HireDate" />
<Condition ColumnName="PersonCategory" Value="2" />
</MappingFragment>
</EntityTypeMapping>
<EntityTypeMapping TypeName="SchoolDataLib.Administrator">
<MappingFragment StoreEntitySet="Person">
<ScalarProperty Name="PersonID" ColumnName="PersonID" />
<ScalarProperty Name="FirstName" ColumnName="FirstName" />
<ScalarProperty Name="LastName" ColumnName="LastName" />
<ScalarProperty Name="AdminDate" ColumnName="AdminDate" />
<Condition ColumnName="PersonCategory" Value="3" />
</MappingFragment>
</EntityTypeMapping>
</EntitySetMapping>
<AssociationSetMapping Name="FK_Department_Administrator"
TypeName="SchoolDataLib.FK_Department_Administrator"
StoreEntitySet="Department">
<EndProperty Name="Person">
<ScalarProperty Name="PersonID" ColumnName="Administrator" />
</EndProperty>
<EndProperty Name="Department">
<ScalarProperty Name="DepartmentID" ColumnName="DepartmentID" />
</EndProperty>
<Condition ColumnName="Administrator" IsNull="false" />
</AssociationSetMapping>
</EntityContainerMapping>
</Mapping>
How to: Define a Model with Table-per-Type Inheritance (Entity Framework)
How to: Add and Modify Objects with Table-per-Type Inheritance (Entity Framework)
How to: Define a Model with Table-per-Hierarchy Inheritance (Entity Framework)