🔥 Pragmatic .NET Code Rules Course is on Presale - 40% off!BUY NOW

Discriminator Column in EF Core: A Quick Guide

Feb 17 2025

Sponsored

• Struggling with slow EF Core performance? Unlock up to 14x faster operations and cut execution time by 94% with high-performance library for EF Core. Seamlessly enhance your app with Bulk Insert, Update, Delete, and Merge—fully integrated into your existing EF Core workflows. Trusted by 5,000+ developers since 2014. Ready to boost your performance? Explore the solution

• With Postman’s AI Agent Builder making waves, I can’t wait to see what’s in store at POST/CON this year. Last year was phenomenal - you remember I attended it in SF. Sign up for updates now! Save the date and join the mailing list here.

Many thanks to the sponsors who make it possible for this newsletter to be free for readers. Become a sponsor.

The Background

What is a Discriminator in Entity Framework? In Entity Framework (EF) Core, a discriminator is a special column used in Table Per Hierarchy (TPH) inheritance to differentiate between multiple entity types stored in a single table. How Discriminators Work? When using inheritance in EF Core, all derived types are mapped to the same database table. To distinguish between different entity types, EF Core automatically adds a discriminator column in the table. The discriminator column stores a string or an integer value representing the type of entity. Let's see it through example.

Discriminator in EF Core – Table Per Hierarchy (TPH)

Example: Single Table Inheritance

Imagine you have a Base Class Vehicle and two derived classes: Car and Bike.

Step 1: Define the Entities

C#
public class Vehicle{ public int Id { get; set; } public string Model { get; set; } = string.Empty;} public class Car : Vehicle{ public int NumberOfDoors { get; set; }} public class Bike : Vehicle{ public bool HasGear { get; set; }}

Step 2: Configure EF Core in DbContext EF Core automatically adds a Discriminator column when you use TPH inheritance.

C#
public class AppDbContext : DbContext{ public DbSet<Vehicle> Vehicles { get; set; } public DbSet<Car> Cars { get; set; } public DbSet<Bike> Bikes { get; set; }  protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { optionsBuilder.UseSqlServer("your_connection_string"); }}

Step 3: Migration and Database Table Run the following commands:

C#
dotnet ef migrations add InitialCreatedotnet ef database update

Generated Table (Single Table for All Entities)

Generated Table Discriminator: Determines if the row is for Car or Bike. NULL values exist for columns that are not applicable to that entity.

Customizing the Discriminator in EF Core

By default, EF Core names the column Discriminator. However, you can customize its name and values. Example: Custom Discriminator Name and Values Modify OnModelCreating in AppDbContext:

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

Effect on Database Table Generated Table with types

Querying Entities with Discriminator

EF Core automatically filters data based on the discriminator. Query All Vehicles

C#
var vehicles = context.Vehicles.ToList();

Returns all records (both Cars and Bikes).

Query Only Cars

C#
var cars = context.Cars.ToList();

EF Core translates this into:

C#
SELECT * FROM Vehicles WHERE VehicleType = 'CarType';

Query Only Bikes

C#
var bikes = context.Bikes.ToList();

EF Core translates this into:

C#
SELECT * FROM Vehicles WHERE VehicleType = 'BikeType';

Changing the Discriminator at Runtime

Sometimes, you may want to change the discriminator dynamically. Example: Convert a Bike to a Car

C#
var bike = context.Bikes.FirstOrDefault(b => b.Id == 2);if (bike != null){ var car = new Car { Id = bike.Id, // Keep the same ID Model = bike.Model, NumberOfDoors = 4 };  context.Bikes.Remove(bike); context.Cars.Add(car); context.SaveChanges();}

The discriminator column value changes from 'BikeType' to 'CarType'.

Using Enum as Discriminator

EF Core 8 and 9 support enums as discriminator values. Step 1: Define an Enum

C#
public enum VehicleType{ Unknown = 0, Car = 1, Bike = 2}

Step 2: Apply Discriminator with Enum

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

Effect on Database Table: Generated Table enum

Performance Considerations of Discriminators

Advantages of TPH (Discriminator-Based) Inheritance:

  1. Faster queries (single table, no joins).
  2. Easier schema management (no multiple tables). Disadvantages of TPH:
  3. Sparse columns (many NULL values in the table).
  4. Potential table bloat (storing different types of data in a single table). Alternative: Table Per Type (TPT) Instead of using a discriminator, you can use TPT where each entity has its own table. csharp modelBuilder.Entity<Car>().ToTable("Cars"); modelBuilder.Entity<Bike>().ToTable("Bikes");

Wrapping Up

EF Core Discriminators enable Table Per Hierarchy (TPH) inheritance.

The discriminator column is used to differentiate entity types within a single table.

You can customize the discriminator name and values. EF Core 8 & 9 allow enum-based discriminators.

Performance considerations should guide whether to use TPH (with discriminators) or TPT..

That's all from me today.

P.S. Follow me on YouTube.

dream BIG!

About the Author

Stefan Djokic is a Microsoft MVP and senior .NET engineer with extensive experience designing enterprise-grade systems and teaching architectural best practices.

There are 3 ways I can help you:

1. Pragmatic .NET Code Rules Course

Stop arguing about code style. In this course you get a production-proven setup with analyzers, CI quality gates, and architecture tests — the exact system I use in real projects. Join here.

Not sure yet? Grab the free Starter Kit — a drop-in setup with the essentials from Module 01.

2. Design Patterns Ebooks

Design Patterns that Deliver — Solve real problems with 5 battle-tested patterns (Builder, Decorator, Strategy, Adapter, Mediator) using practical, real-world examples. Trusted by 650+ developers.

Just getting started? Design Patterns Simplified covers 10 essential patterns in a beginner-friendly, 30-page guide for just $9.95.

3. Join 20,000+ subscribers

Every Monday morning, I share 1 actionable tip on C#, .NET & Architecture that you can use right away. Join here.

Join 20,000+ subscribers who mass-improve their .NET skills with actionable tips on C#, Architecture & Best Practices.

Subscribe to
TheCodeMan.net

Subscribe to the TheCodeMan.net and be among the 20,000+ subscribers gaining practical tips and resources to enhance your .NET expertise.