30
votes

I'm using EF Core with database-first approach using the "Scaffold-DbContext"-command to generate my DbContext / Entities.

How can I instruct Scaffold-DbContext that a certain field in a certain table should generate code to use an Enum instead of just an int?

This is how you used to do it in regular EF: https://www.devu.com/cs-asp/lesson-69-mapping-enum-types-entity-properties-framework-designer/

Example

This enum is already defined in code:

public enum StateEnum {
  Ok = 1,
  Fail = 2
}

This is what Scaffold-DbContext gives me

public partial class Foo
{
    public int Id { get; set; }
    public int State { get; set; }
}

This is what I want it to create:

public partial class Foo
{
    public int Id { get; set; }
    public StateEnum State { get; set; }
}
8
At present there is no way to create enum property while running scaffold-dbcontext. It is simply because, enums are stored as int (or enum's underlying type in database) and when scaffolding the model, EF looks at metadata hence has no info about the column being int vs enum. You can always change the type of property to enum from int after scaffolding and it would just work fine.Smit
Possible duplicate of Does EF7 support enums?schnitty

8 Answers

28
votes

Starting with Entity Framework Core 2.1, EF supports Value Conversions to specifically address scenarios where a property needs to be mapped to a different type for storage.

Specifically for Enums, you can use the provided EnumToStringConverter or EnumToNumberConverter.

29
votes

Doesn't value conversion in EF Core 2.1 do what you need now?

https://docs.microsoft.com/en-us/ef/core/modeling/value-conversions

Quick Example:

  entity.Property(e => e.MyEnumField)
            .HasMaxLength(50)
            .HasConversion(
                v => v.ToString(),
                v => (MyEnum)Enum.Parse(typeof(MyEnum),v))
                .IsUnicode(false);
11
votes

I got here because of the question title. Not sure if this works in the "Scaffold-DbContext" but it does in DbContext (Microsoft.EntityFrameworkCore 2.0.1.0) by setting the base type of the enum explicitly even if the default underlying type of enumeration elements is int. You can use Fluent API to set default values with it too (especially this case where the enum starts with 1).

public enum StateEnum : int
{
    Ok = 1,
    Fail = 2
}

The approved types for an enum are byte, sbyte, short, ushort, int, uint, long, or ulong.

So I think this would work for any of these. enum (C# Reference)

public class MyDbContext : DbContext
{      
    protected override void OnModelCreating(ModelBuilder builder) 
    {
        builder.Entity<Foo>().Property(x => x.State).HasDefaultValue(StateEnum.Ok);
    }
}
4
votes

Currently, EF core does not support enums. Code like this:

public class MyDbContext : DbContext
{      
    protected override void OnModelCreating(ModelBuilder builder) 
    {
        builder.Entity<StateEnum>(e => {...});
    }
}

does not compile with the message:

CS0452 C# The type must be a reference type in order to use it as parameter 'TEntity' in the generic type or method

Solution: you can use enumeration classes instead

4
votes

Try this solution:

public enum StateEnum {
      Ok = 1,
      Fail = 2
}

public partial class Foo
{
    public int Id { get; set; }
    public int StateId { get; set; }
    public StateEnum State
    {
        get => (StateEnum)StateId;
        set => StateId = (int)value;
    }
}
1
votes

Following the link provided in the accepted answer, I used pre-defined conversion for a code first approach and it works, using Entity Framework Core 5:

modelBuilder.Entity<Model>(model => {
    model.Property(m => m.EnumType)
        .HasConversion<int>();
});
1
votes

The accepted answers are not solving the problem. The questions states "How can I instruct Scaffold-DbContext that a certain field in a certain table should generate code to use an Enum instead of just an int?" A lot of answers are stating that with Entity Framework Core 2.1, there is now support for Value Conversions. That is useful if you use Code-first, NOT Database-first. The Scaffold-DbContext will overwrite DBContext every time. Personally i have no problem with the enum being a integer in the database. But I don't want to use integers within the code.

You could either

  1. Change the DBContext to use ValueConversion. You will loose that code every time you scaffold.
  2. Change the Ints in you Entities to the specific Enum. It works fine but you will also loose that when you scaffold.
  3. Create a partial class where you create a methods called GetStateEnum() and SetStateEnum(StateEnum stateEnum) etc. It's quite verbose but it will stay between scaffolding.
  4. There is something called Source Generators that maybe could solve this in the future. I have not found an easy solution to this.

I chose alternative 2. It's the most simple one I have found so far and it's quite easy to revert the classes with git-compare. I can change all my enums within a minute.

If someone has a better solution. Please tell me.

0
votes

You can have you enum (U) and an entity (T) representing enum values in the database

public static T[] BuildEntityObjectsFromEnum<T, U>() where U: Enum where T : new()
    {
        var listObjectsToReturn = new List<T>();
        Dictionary<string, int> dictionary = Enum.GetValues(typeof(U)).Cast<U>().ToDictionary(t => t.ToString(), t =>  Convert.ToInt32(t));

        foreach (var item in dictionary)
        {
            var newObject = new T();
            Type classType = typeof(T);
            classType.GetProperties()[0].SetValue(newObject, item.Value); // Enum int id
            classType.GetProperties()[1].SetValue(newObject, item.Key); // Enum string value
            listObjectsToReturn.Add(newObject);
        }
        return listObjectsToReturn.ToArray();
    }

Then you can seed the table from the enum

modelBuilder.Entity<T>().HasData(BuildEntityObjectsFromEnum<T,U>());