Course contentsShow
← C#
Lesson 1044 of 1,90810. Entity Framework Core ModelingFree lesson

Table-per-Type Inheritance

Normalize polymorphic models into distinct tables while maintaining relational integrity.

In this lesson, you will learn how to normalize polymorphic class hierarchies into separate, related tables using Entity Framework Core's Table-per-Type (TPT) mapping.

The Problem with Shared Tables

Previously, we explored Table-per-Hierarchy (TPH), which collapses an entire inheritance tree into a single table with a discriminator column. While performant, TPH can lead to "sparse" tables filled with null values if your child classes have many unique properties. Table-per-Type (TPT) solves this by giving every class in the hierarchy its own dedicated table.

How TPT Works

In TPT, the base class has a table, and each derived class has its own table containing only the properties specific to that derived type. To maintain relational integrity, the tables for the derived classes share the same Primary Key as the base class. This key also acts as a Foreign Key pointing back to the base table, effectively "linking" the pieces of the object together.

Consider a User hierarchy:

public abstract class User {
    public int Id { get; set; }
    public string Email { get; set; }
}

public class Employee : User {
    public string EmployeeCode { get; set; }
}

public class Customer : User {
    public string LoyaltyLevel { get; set; }
}

To configure this using the Fluent API in your DbContext, you explicitly map each entity to its own table:

protected override void OnModelCreating(ModelBuilder modelBuilder) {
    modelBuilder.Entity<User>().ToTable("Users");
    modelBuilder.Entity<Employee>().ToTable("Employees");
    modelBuilder.Entity<Customer>().ToTable("Customers");
}

When to Use It

EF Core handles the necessary JOIN operations automatically when you query these entities. Use TPT when you want to enforce strict database-level constraints (like NOT NULL on child-specific columns) or when you prefer a highly normalized schema where each table only contains the data relevant to its specific type. Because it requires joins to reconstruct objects, consider your performance needs when choosing between TPH (speed) and TPT (normalization).

Key Takeaway: Table-per-Type inheritance improves database normalization by mapping each class in a hierarchy to its own table, using shared Primary Keys to maintain relational integrity across the model.