5

I am trying to use EntityFrameworkCore ORM to interact with my databases. By default, EntityFrameworkCore seems to store enum as int instead of string.

However, I would like to store the value in the database as a string. I can see that EntityFrameworkCore ships with a converter called EnumToStringConverter.

I am trying to use reflection to setup the model builder so I don't have to manually build each model.

The issue that I am running into is that EnumToStringConverter accepts a generic type which must be an enum. But since I am trying to use reflection here I am unable to pass the enum type when creating the converter

Here is what my code look like so far

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    base.OnModelCreating(modelBuilder);

    // Get all DbSet<> properties that are defined in the DbContext
    var modelTypes = typeof(DataContext).GetProperties()
                                        .Where(x => x.PropertyType.IsGenericType && x.PropertyType.GetGenericTypeDefinition() == typeof(DbSet<>))
                                        .Select(x => x.PropertyType.GetGenericArguments().First())
                                        .ToList();

    foreach (Type modelType in modelTypes)
    {
        var properties = modelType.GetProperties();

        foreach (var property in properties)
        {
            if (IsPrimaryKey(property))
            {
                // At this point we know that the property is a primary key
                modelBuilder.Entity(modelType)
                            .Property(property.Name)
                            .UseSqlServerIdentityColumn()
                            .Metadata.BeforeSaveBehavior = PropertySaveBehavior.Ignore;

                continue;
            }

            if (property.PropertyType.IsEnum)
            {
                // At this point we know that the property is an enum.
                // Add the EnumToStringConverter converter to the property so that
                // the value is stored in the database as a string instead of number 
                var converter = new EnumToStringConverter(); // if somehow I can change this code to something like var `new EnumToStringConverter(property.PropertyType);` the code would work

                modelBuilder.Entity(modelType)
                            .Property(property.Name)
                            .HasConversion(converter);

                continue;
            }

        }
    }
}

The only issue with the above code is how EnumToStringConverter is constructed. If somehow I can provide a Type to the constructor of the EnumToStringConverter instead of passing it as a generic argument that would solve the problem.

Junior
  • 11,602
  • 27
  • 106
  • 212

3 Answers3

7

As explained in the Pre-defined conversions documentation section:

For common conversions for which a built-in converter exists there is no need to specify the converter explicitly. Instead, just configure which provider type should be used and EF will automatically use the appropriate build-in converter. Enum to string conversions are used as an example above, but EF will actually do this automatically if the provider type is configured:

followed by an example.

Following that, you could simply use:

if (property.PropertyType.IsEnum)
{
    // At this point we know that the property is an enum.
    // Add the EnumToStringConverter converter to the property so that
    // the value is stored in the database as a string instead of number 
    modelBuilder.Entity(modelType)
        .Property(property.Name)
        .HasConversion<string>(); // <--

    continue;
}
Ivan Stoev
  • 195,425
  • 15
  • 312
  • 343
  • That did it. I missed it – Junior Jun 15 '18 at 18:00
  • How to do this by default so you don't have to write this out 200 times? – Jeremy Holovacs Feb 22 '19 at 01:12
  • 1
    @JeremyHolovacs you can do it like this: https://stackoverflow.com/questions/50727860/ef-core-2-1-hasconversion-on-all-properties-of-type-datetime just change the inner part of the code with the answer from here. But if you ask this in a separate SO question I am happy to give you the precise answer! – Dominik Apr 08 '19 at 19:33
  • or as an attribute place [Column(TypeName = "nvarchar(32)")] to the enum property. this will automatically save it as a string. – CleanCoder Apr 20 '20 at 11:22
4

As of EF core v6.0.0-preview6, there is a more elegant solution to register a ValueConverter globally. Since EFcore already ships with a EnumToStringConverter, just add these lines to your DbContext class:

protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder)
{
    // Instead of numeric conversion that EFcore uses by default
    configurationBuilder.Properties<Enum>().HaveConversion<string>();
}

and all properties of type Enum will be serialized as string, rather than numbers.

Dynalon
  • 6,577
  • 10
  • 54
  • 84
1

was searching for the same, but didn't want to use the fluent approach.

plase the following to your enum property.

 [Column(TypeName = "nvarchar(32)")] 

this will automatically save it as a string. if placed in a base class this applies for all derived entities.

you could also register a type configuration

public class BaseEntityTypeConfiguration : IEntityTypeConfiguration<BaseEntity>
{
    public void Configure(EntityTypeBuilder<BaseEntity> builder)
    {
        builder.Property(p => p.YourEnumProp).HasConversion<string>();
    }
}

then you register it on the modelBuilder

mb.ApplyConfiguration(new BaseEntityTypeConfiguration());
CleanCoder
  • 2,406
  • 1
  • 10
  • 35