34

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; }
}
ASh
  • 32,398
  • 9
  • 53
  • 74
Bassebus
  • 582
  • 1
  • 5
  • 14
  • 5
    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 May 31 '17 at 05:56
  • Possible duplicate of [Does EF7 support enums?](https://stackoverflow.com/questions/35298829/does-ef7-support-enums) – schnitty Apr 19 '18 at 06:06

8 Answers8

32

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);
MrKobayashi
  • 1,007
  • 2
  • 12
  • 19
  • This does work, but does not consider the OP's original requirements of using the Scaffold command which will overwrite the contents of the `OnModelCreating` method every time it is ran. – Ryan Griffith Apr 24 '21 at 17:37
31

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.

Dejan
  • 7,717
  • 7
  • 57
  • 105
  • 2
    Finally! Thanks! – Bassebus Sep 26 '18 at 15:32
  • 2
    The problem with all of these answers is that they store the Enum as either a string or number column of the enum's parent table. From a database point of view, this is wrong. From the database point of view, this enum should be stored as an ID field in the parent table, which references a lookup table via a foreign key. – Pavel Dec 04 '18 at 20:59
  • 9
    I disagree. An enum is CODE , so there is no database representation otherwise you can simply add another value to the database which renders then the code invalid as that enum value is unknown. – user1029883 Dec 26 '18 at 16:28
  • 2
    However, there is a big caveat here: "Use of value conversions may impact the ability of EF Core to translate expressions to SQL. A warning will be logged for such cases. Removal of these limitations is being considered for a future release" https://docs.microsoft.com/en-us/ef/core/modeling/value-conversions – Cito Jan 09 '19 at 10:44
  • 10
    Scaffold-DbContext will create/overwrite my DbContext, so it overrides the `OnModelCreating`, how do you handle that? – jsgoupil Apr 06 '20 at 00:34
  • @jsgoupil The scaffolded DbContext is a `partial class` , so you can add your own overridden methods in a separate file that won't be overwritten. – Dai Nov 18 '21 at 01:33
  • @Dai Nope, I need to add things inside `OnModelCreating` – jsgoupil Nov 18 '21 at 21:43
11

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);
    }
}
A.J.Bauer
  • 2,625
  • 1
  • 25
  • 34
  • I had a legacy table in our DB which was using a small int to represent an enum, so i needed to change the enum to inherit from short: ( MyType: short) for ef core to convert it correctly. Thanks!! – Timmy Fuller Jul 19 '18 at 09:25
  • Alternatively, `.HasConversion();` would work as opposed to `.HasDefaultValue(..)` – Fraze Nov 01 '18 at 18:26
6

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.

Bjorn
  • 113
  • 2
  • 9
4

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

Ruslan Hamzatov
  • 380
  • 3
  • 7
4

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;
    }
}
Yann Duran
  • 3,786
  • 1
  • 23
  • 23
Aliaksei Zhukau
  • 199
  • 2
  • 12
1

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>());
sofsntp
  • 1,684
  • 21
  • 27
1

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>();
});
netotz
  • 111
  • 1
  • 4
  • 9