SoFunction
Updated on 2025-03-06

Entity Framework mapping TPH, TPT, TPC and inheritance classes

1. TPH

Table Per Hierarchy (default, one table per hierarchy)

Each hierarchy shares a table, and each property of the class must be nullable.

1. Default behavior

Create only one table and map all attributes in the base class and subclass into columns in the table.
Create a table for the base class and all subclasses, and all attributes in the base class and subclass are mapped to a column in the table.

By default, a column called Discriminator is created in this table, with type nvarchar and length 128. When storing a base class or subclass, use the class name as the value of the Discriminator column.

2. Fluent API modify default behavior

The type parameter passed in the Map method is the class name of the subclass. Requires is used to specify the name of the Discriminator column. HasValue is used to specify its type and the corresponding value of each subclass.

<Course>() 
    .Map<Course>(m => ("Type").HasValue("Course")) 
     .Map<OnsiteCourse>(m => ("Type").HasValue("OnsiteCourse"));

2. TPT

Table Per Type (one table for each class)

1. Default behavior

Create a table for the base class and each subclass, and each table corresponding to the subclass contains only columns corresponding to the attributes unique to the subclass.
The table of a subclass contains only attributes that are unique to the subclass, and the sub-table also stores a foreign key that joins the sub-table with the base table.

2. Fluent API modify default behavior

We can use the Map method to force Code First to use the TPT method, because Code First uses the TPH method by default.

<Course>().ToTable("Course"); 
<OnsiteCourse>().ToTable("OnsiteCourse");

III. TPC

Table Per ConCrete Type (one table for each specific type)

Each specific derived class has one table, no base table. Not recommended.

1. Default behavior

In addition to the attributes unique to the subclass, there are also tables corresponding to the attributes of the base class. The base class can be abstract.

2. Fluent API modify default behavior

The MapInheritedProperties method can force Code First to use the TPC method.

Note: Because tables belonging to the TPC inheritance hierarchy do not use the same primary key, turn off the identification of the primary key attribute to avoid inserting duplicate entity keys for different subtables.

<Course>()
     .Property(c => )
     .HasDatabaseGeneratedOption();

<OnsiteCourse>().Map(m =>
 {
     ();
     ("OnsiteCourse");
 });

<OnlineCourse>().Map(m =>
 {
     ();
     ("OnlineCourse");
 });

4. Entity splitting

Allows properties of an entity type to be scattered across multiple tables.
Entity splitting maps a portion of the properties to a specific table by calling the Map method multiple times.
In the following example, the Department entity is split into two tables: Department and DepartmentDetails.

<Department>() 
    .Map(m =>
    {
        (t => new { ,  });
        ("Department");
    }).
     Map(m =>
    {
        (t => new { , , ,  });
         ("DepartmentDetails");
     });

5. Table splitting

Two entity types map to the same table.

1. Both classes must share the same primary key.
2. The relationship between two classes must be mapped as a one-to-one relationship between tables.

&lt;OfficeAssignment&gt;().HasKey(t =&gt; );  //Share primary key&lt;Instructor&gt;() .HasRequired(t =&gt; ).WithRequiredPrincipal(t =&gt; );//One-to-one relationship
&lt;Instructor&gt;().ToTable("Instructor");
&lt;OfficeAssignment&gt;().ToTable("Instructor");

6. Specify the class as a complex type

1. Specify method:

DataAnnotations method:

[ConlexType]
public Details details;

Or FluentAPI:

<Details>();

Notice:
1. Complex type classes cannot have primary keys.
2. Complex types can only contain attributes of .net basic types.
3. When using a class of complex types, you can only package an instance of a complex type, and you cannot use a collection of complex types.

2. Configure properties of complex types

(1) Property can be called on ComplexTypeConfiguration.

<Details>() .Property(t => ).HasMaxLength(20);

or

public class AddressComplexTypeConfiguration:ComplexTypeConfiguration<Address>
    {
        public AddressComplexTypeConfiguration()
        {
            Property(a => ).HasColumnName("Country").HasMaxLength(100);
            Property(a => ).HasColumnName("ZipCode").HasMaxLength(6);
         }
     }

(2) You can also use point notation to access properties of complex types.

<OnsiteCourse>().Property(t => ) .HasMaxLength(20);

7. DataBase initialization

1. Call method:

Generally, the entrance of the Main application is called in the following places:

  • As long as the database mapping configured by the Fluent API changes or the model in the domain changes, the previous database is deleted and the database is re-established according to the new configuration.
    (new DropCreateDatabaseIfModelChanges<BreakAwayContext>());
  • A new database is created based on the database connection configuration only when there is no database. This configuration is mainly used in the production environment.
    (new CreateDatabaseIfNotExists<BreakAwayContext>());
  • Regardless of whether the database mapping or model changes or not, the database is re-deleted and rebuilt according to the configuration.
    (new DropCreateDatabaseAlways<BreakAwayContext>());

2. More flexible way to specify the database initialization method through configuration files:

<?xml version="1.0"?>
 <configuration>
 <appSettings>
   <add key="DatabaseInitializerForTypeOrderSystemContext" value="[[OrderSystemContext]], EntityFramework" /> 
 </appSettings>
 </configuration>

3. Customize the database initialization class

Through a custom initialization class, some basic data can also be inserted into the database after creating the database.

public class DropCreateOrderDatabaseWithSeedValueAlways : DropCreateDatabaseAlways<OrderSystemContext>
   {
       protected override void Seed(OrderSystemContext context)
       {
            (new ProductCatalog { CatalogName = "DELL E6400", Manufactory = "DELL", ListPrice = 5600, NetPrice = 4300 });
            (new ProductCatalog { CatalogName = "DELL E6420", Manufactory = "DELL", ListPrice = 7000, NetPrice = 5400 });
        }
    } 

(new DropCreateOrderDatabaseWithSeedValueAlways());

This is all about this article about Entity Framework mapping TPH, TPT, TPC and inheritance classes. I hope it will be helpful to everyone's learning and I hope everyone will support me more.