Thursday, April 11, 2024

THE CASE OF INHERITANCE IN ENTITY FRAMEWORK CORE

The Investigation

Entity Framework Core simplifies working with databases for .NET developers. Instead of creating tables and stored procedures manually, EF creates tables automatically from classes created in C# and generates queries to fetch the data and update it. EF is known as an object relational mapper (ORM). What is that? Well, C# is an object-oriented language and SQL Server is a relational database, EF is the bridge or translator, if you will. But is it actually possible to create classes in C# with inheritance and have EF magically translate it into a relational database? The DotNet Detective investigates.

To investigate, we create a project with some simple classes using .NET/EF Core 8. At the root is a User class, the children are Regular User, Premium User, and Admin User. Regular User also has a child class: Short Term User. It looks like this:

We also need a Database Context with a connection string. In this case, we are connecting to SQL Server. Finally, we add a bit of code to create one of each kind of user. 

After we create our first migration and run the project to populate the data, we can see the result, a single table that looks like this:



As we can see, all classes have been combined into one table and a Discriminator column has been added to distinguish them. While this solution is functional, it is also pretty horrific database design. Not much relational going on here and all those NULLs!

The Discovery

Ideally, we would see one table for each class. In the case of inheritance, a JOIN query would combine the data from the Parent class with the Child class.

In order to achieve this, we need to add an event handler for the OnModelCreating event. And here we specify each table to be created:

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {

            modelBuilder.Entity<User>().ToTable("Users");
            modelBuilder.Entity<RegularUser>().ToTable("RegularUsers");
            modelBuilder.Entity<PremiumUser>().ToTable("PremiumUsers");
            modelBuilder.Entity<AdminUser>().ToTable("AdminUsers");
            modelBuilder.Entity<ShortTermUser>().ToTable("ShortTermUsers");
        }

Let’s create another migration and look at the result:

Well, look at that. That’s more like it. But what about the query? Let’s retrieve a Short Term User and see if the query joins all three tables in the inheritance chain:

ShortTermUser stUser = context.ShortTermUsers.FirstOrDefault();

And the output window shows us the query:

SELECT TOP(1) [u].[Id], [u].[CreatedDate], [u].[Email], [u].[Retired], [r].[ItemLimit], [s].[ExpirationDate]

FROM [Users] AS [u]

INNER JOIN [RegularUsers] AS [r] ON [u].[Id] = [r].[Id]

INNER JOIN [ShortTermUsers] AS [s] ON [u].[Id] = [s].[Id]

The Arresting Conclusion

Entity Framework Core can indeed be used as a bridge between object-oriented code and relational data stores. Note that this feature is not available in EF 5 and earlier.

To learn more, follow these links:

https://learn.microsoft.com/en-us/ef/core/modeling/inheritance

https://learn.microsoft.com/en-us/aspnet/core/data/ef-mvc/inheritance?view=aspnetcore-8.0


THE CASE OF INHERITANCE IN ENTITY FRAMEWORK CORE

The Investigation Entity Framework Core simplifies working with databases for .NET developers. Instead of creating tables and stored procedu...