How to Represent Table per Hierarchy Inheritance in EF with Discriminator Column

Listen to this Post

Featured Image
When working with Entity Framework Core (EF Core), the Discriminator column is a powerful feature for implementing Table per Hierarchy (TPH) inheritance. This approach allows storing different derived entities (e.g., Car, Bike) in a single database table while distinguishing them using a discriminator value.

Key Concepts

  • Shared Properties: Base class properties (e.g., Id, Model) are stored in a single table.
  • Unique Properties: Subclass-specific properties (e.g., `NumberOfDoors` for Car, `HasGear` for Bike) are stored in the same table.
  • Automatic Instantiation: EF Core uses the discriminator to instantiate the correct object type when querying.

Implementation Steps

1. Define Base and Derived Classes

public class Vehicle
{
public int Id { get; set; }
public string Model { get; set; }
}

public class Car : Vehicle
{
public int NumberOfDoors { get; set; }
}

public class Bike : Vehicle
{
public bool HasGear { get; set; }
}

2. Configure TPH in DbContext

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Vehicle>()
.HasDiscriminator<string>("VehicleType")
.HasValue<Car>("Car")
.HasValue<Bike>("Bike");
}

3. Querying Polymorphically

var vehicles = context.Vehicles.ToList();
foreach (var vehicle in vehicles)
{
if (vehicle is Car car)
Console.WriteLine($"Car with {car.NumberOfDoors} doors");
else if (vehicle is Bike bike)
Console.WriteLine($"Bike with gear: {bike.HasGear}");
}

4. Inserting Records

context.Vehicles.Add(new Car { Model = "Tesla", NumberOfDoors = 4 });
context.Vehicles.Add(new Bike { Model = "BMX", HasGear = true });
context.SaveChanges();

You Should Know: Advanced EF Core Commands & Practices

1. Changing the Discriminator Column Name

By default, EF Core uses `Discriminator`. To customize:

modelBuilder.Entity<Vehicle>()
.HasDiscriminator<string>("VehicleType");

2. Raw SQL for Performance Optimization

var cars = context.Vehicles
.FromSqlRaw("SELECT  FROM Vehicles WHERE VehicleType = 'Car'")
.ToList();

3. Handling Complex Inheritance

For multiple levels:

modelBuilder.Entity<Vehicle>()
.HasDiscriminator<string>("VehicleCategory")
.HasValue<ElectricCar>("ElectricCar")
.HasValue<SportsCar>("SportsCar");

4. Indexing the Discriminator for Faster Queries

modelBuilder.Entity<Vehicle>()
.HasIndex(v => EF.Property<string>(v, "VehicleType"));

5. Using Discriminator in LINQ

var bikes = context.Vehicles
.Where(v => EF.Property<string>(v, "VehicleType") == "Bike")
.ToList();

6. Migration Commands

dotnet ef migrations add AddVehicleHierarchy
dotnet ef database update

7. Querying Without Loading All Types

var carsOnly = context.Vehicles.OfType<Car>().ToList();

What Undercode Say

The Discriminator column in EF Core simplifies polymorphic data storage while maintaining performance. Key takeaways:

✔ Single-table efficiency – Reduces JOIN operations.

✔ Automatic type resolution – EF Core handles instantiation.
✔ Flexible querying – Supports LINQ, raw SQL, and filtering.

For large-scale apps, consider:

  • Indexing discriminators for faster queries.
  • Using TPC (Table per Concrete Class) if subclass properties vary significantly.

Expected Output

A structured EF Core implementation with optimized queries, migrations, and polymorphic data handling.

🔗 Learn More: EF Core Inheritance Strategies

References:

Reported By: Djokic Stefan – Hackers Feeds
Extra Hub: Undercode MoN
Basic Verification: Pass ✅

Join Our Cyber World:

💬 Whatsapp | 💬 Telegram