By default, Entity Framework Core stores and retrieves .NET enumerations as their underlying integer values for SQL databases, primarily for performance and storage optimization reasons.
Nevertheless, if there is a requirement to store enums within your data model as strings at the database level, it is important to have a suitable way to configure this behaviour globally to ensure that all enum properties are converted.
In a prior article, I already blogged about how to Store and retrieve enums as strings with Entity Framework Core. However, the current article documents a better way to convert enums to strings that is not only a superior approach in my estimation but is also significantly simpler.
The old way
In my prior article, I covered how to apply the enum to string conversion on a per-context basis using the following code which relied on some hand-written Reflection logic to handle both standard and nullable enum properties.
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; public class TodoContext : DbContext { public DbSet<Todo> Todos { get; set; } public DbSet<User> Users { get; set; } protected override void OnModelCreating(ModelBuilder modelBuilder) {
base.OnModelCreating(modelBuilder);
foreach (var entityType in modelBuilder.Model.GetEntityTypes()) { foreach (var property in entityType.GetProperties()) { if (property.ClrType.IsEnum) { var type = typeof(EnumToStringConverter<>).MakeGenericType(property.ClrType); var converter = Activator.CreateInstance(type, new ConverterMappingHints()) as ValueConverter; property.SetValueConverter(converter); } else if (Nullable.GetUnderlyingType(property.ClrType)?.IsEnum == true) { var type = typeof(EnumToStringConverter<>).MakeGenericType(Nullable.GetUnderlyingType(property.ClrType)!); var converter = Activator.CreateInstance(type, new ConverterMappingHints()) as ValueConverter; property.SetValueConverter(converter); } } } } }
While this isn’t a huge amount of code, it requires some understanding of Value Converters, Reflection, and the Entity Framework Core ModelBuilder
to comprehend what is going on.
Additionally, while the above code handles a lot of scenarios, unfortunately, it isn’t able to handle enums that are defined within any base classes that your entities happen to inherit from.
By way of example, consider the following set of enumeration types and classes.
public enum TodoStatus { New = 0, InProgress = 1, Completed = 2 } public enum EntityStatus { Inactive = 0, Active = 1 } public class Entity { public int Id { get; set; } public EntityStatus EntityStatus { get; set; } } public class Todo : Entity { public int UserId { get; set; } public string Title { get; set; } = ""; public TodoStatus Status { get; set; } }
In this rudimentary example, the previously documented enum to string conversion code would successfully apply the conversion to the Status
property on the Todo
class, but the inherited EntityStatus
property would continue to be stored as an integer in the database.
The usefulness of this ‘global’ enum to string conversion logic is very limited in cases where you have entity classes that inherit from other classes containing enums, which is quite a common scenario.
A better way
So, what if there was a better way to convert enums to strings across the board?
Well, as it turns out, there is! And as a bonus, it’s super simple to implement.
With a refactored version of the previous example, here’s all you need to do.
using Microsoft.EntityFrameworkCore; public class TodoContext : DbContext { public DbSet<Todo> Todos { get; set; } public DbSet<User> Users { get; set; } protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder) { base.ConfigureConventions(configurationBuilder); configurationBuilder.Properties<Enum>().HaveConversion<string>(); } }
Yes, that’s really all there is to it!
The key line of code is as follows.
configurationBuilder.Properties<Enum>().HaveConversion<string>();
Here we are accessing all properties within the data model that are of type Enum
and then calling the HaveConversion
method, specifying string
as the generic type. As a result, all enums will be converted to strings automatically, including enums from inherited entity classes.
Note that this configuration will be applied to both standard enum and nullable enum types without the need for any special logic to account for the nullable case.
If you are using Entity Framework Core Migrations, after making this change you can add a new migration and check that an alter command is generated for all enums in your data model.
I love how simple this approach is and it’s good to know that it’s also the approach that is recommended going forward by the current Principal Software Engineer on the Entity Framework team at Microsoft.
It’s useful to remember that even though we’re specifying Enum
and string
as the type parameters to the ModelConfigurationBuilder
methods, Entity Framework Core will still register a Value Converter that is suitable for the conversion based on the generic type arguments.
In this case, the EnumToStringConverter
will be selected by the framework, in a similar manner to the older approach. The difference this time is that we are letting Entity Framework Core do the heavy lifting for us and abstracting away the complicated logic that is required for correctly identifying all enum-type properties in the data model.
Note that you could also pass EnumToStringConverter
as the generic type parameter to the HaveConversion
method.
Summary
In this article, I documented a better way to convert enums to strings using Entity Framework Core.
I started by covering the old way of configuring the conversion and highlighted some of its limitations.
I then proceeded to demonstrate a better approach which is significantly simpler and also caters for absolutely all enums within your data model.
For some further context, I recommend that you check out my prior Store and retrieve enums as strings with Entity Framework Core article which discusses additional topics such as the pros and cons of storing enums as strings at the database level, per-property configuration, and backwards compatibility considerations. I have updated the prior article with a link to this article to help readers get access to a better way of converting enums to strings on a per-context basis.
Comments