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


Thursday, January 18, 2024

THE CASE OF THE MYSTERIOUS CLASS LIBRARY

The Investigation

I just found a dusty, purple flash drive behind my desk. It doesn't look familiar. I wipe it clean, pop it into the PC, and run a virus check. Clean. When we open it we see a number of files, all DLL class libraries. The first one is "Zodiac.dll" Whoa, like the Zodiac Killer? We need to investigate. It is at moments like this that .NET Reflection comes to mind.

.NET Reflection allows us to inspect class libraries and discover types, methods, and properties at runtime. Furthermore, we can instantiate the types, call the methods, and change the properties.

First, we want to reveal what types it contains. We create a new .NET Core 8 console application and then copy the Zodiac.dll to the project folder. Let's load it:

using System.Reflection;

var DLL = Assembly.LoadFile(@"<AbsolutePath>\Zodiac.dll");

We want to list the types, if they are public, and what the base type is:

foreach (Type type in DLL.GetTypes())
{
    Console.WriteLine(type.Name + " (" + (type.IsPublic ? "public" : "not public") + 
                        "), Base Type: " + type.BaseType);
}

The Discovery



Aha! Chinese Zodiac. How are these types connected? Looking at the base types, we can see that CancerZodiacModel inherits from ZodiacModel, which in return inherits from  BaseZodiacModel.

Next, let's get the methods for the Chinese Zodiac class, the parameters, and the return types:

Type type = DLL.GetType("Horoscope.ChineseZodiac");  
foreach (MethodInfo info in type.GetMethods())
{
    var paramInfo = "";
    foreach (var param in info.GetParameters())
    {
        paramInfo += param.ParameterType.Name + " " + param.Name;
    }
    Console.WriteLine(info.Name + " " + paramInfo);
}

Just one method: GetZodiacSignForDate, one parameter: requestedDateTime, and a return type of ChineseZodiacModel.

The Arresting Conclusion

Of course we could have just referenced this DLL in Visual Studio to discover it's public types, methods, and properties. The power of Reflection lies in the fact we can do this analysis at runtime and dig deeper to show private types, methods, and properties. This allows us to easily write a bit of code to examine all the DLLs in a folder and produce a report.

Now let's invoke the method and see what we get:

Type type = DLL.GetType("Horoscope.ChineseZodiac");
var method = type.GetMethod("GetZodiacSignForDate");
DateTime date = DateTime.Parse("1/18/24");
dynamic result = method.Invoke(type, new object[] { date });
var englishName = result.ZodiacEnglishTranslation;
var personality = result.ZodiacPersonality;
Console.WriteLine(date.ToShortDateString() + ": " + englishName + ", " + personality);


Note the use of the dynamic type of the result. The result is of type "ChineseZodiacModel" but as we haven't referenced the DLL, we can't use the type in the declaration. Dynamic let's us use the properties "ZodiacEnglishTranslation" and "ZodiacPersonality" without getting a runtime error.

So much for a quick taste of Reflection in .NET.

To learn more, follow these links:

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...