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

Working with TPC Inheritance

Structure polymorphic hierarchies using Table-per-Concrete-type for optimized individual class retrieval.

In this lesson, you will learn how to implement the Table-per-Concrete-type (TPC) inheritance pattern in EF Core to ensure that each leaf class in your hierarchy gets its own dedicated database table.

Understanding TPC Inheritance

When you use TPC, every non-abstract class in your hierarchy corresponds to a fully independent table in your database. Unlike Table-per-Hierarchy (TPH), where all data lives in one wide table with a discriminator, or Table-per-Type (TPT), which joins tables across relationships, TPC duplicates the shared columns (like Id or CreatedAt) across every table.

This approach is highly optimized for performance when you query for a specific type, as EF Core doesn't need to perform expensive JOIN operations or filter by a discriminator column.

Implementing TPC Mapping

To configure this in your DbContext, you must explicitly tell EF Core that your base class is part of a hierarchy and that each concrete class should have its own storage. You typically do this within the OnModelCreating method using the Fluent API.

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Payment>()
        .UseTpcMappingStrategy();

    modelBuilder.Entity<CreditCardPayment>().ToTable("CreditCardPayments");
    modelBuilder.Entity<PayPalPayment>().ToTable("PayPalPayments");
}

In this setup, Payment is the base class. Because we used .UseTpcMappingStrategy(), EF Core creates two distinct tables: CreditCardPayments and PayPalPayments. Both tables contain the columns defined in the base Payment class as well as the properties unique to their specific type.

When to use TPC

TPC is an excellent choice when you frequently query for specific concrete types rather than the base type. If you write a query like context.CreditCardPayments.ToList(), EF Core generates a simple SELECT statement against only one table. If you query the base Payment type, EF Core will automatically perform a UNION ALL across all related tables to gather the results. Note that because each table is independent, you must ensure that your primary key strategy (such as identity or sequence generation) does not cause ID collisions across the different tables.

Key Takeaway: Use TPC inheritance to provide each concrete class its own dedicated table, eliminating JOINs for individual type queries at the cost of duplicating shared columns across tables.