72

This is my Model Class where we have a Type, which could be a Zombie or Human.

public class User
{
    public int ID { get; set; }
    public string Name { get; set; }
    public Type Type { get; set; }
    public List<Weapon> WeaponsInList { get; set; }
}  

public enum Type
{   
    [Description("Zombie")]
    Zombie,
    
    [Description("Human")]
    Human
}

Currently, it is saving data in Int.

enter image description here

I want to save the data as Human and Zombie, not with int.

5
  • 2
    An enumeration is a static object, there's no point in saving it to the database... Really you should define the items in the database and then generate your enum in code using a T4 template. Commented Sep 12, 2015 at 18:49
  • 5
    An enum value is an int. You should really save it as so. Unless you have a very good reason to have it as a string in the DB.. Which I suppose you don't. Commented Sep 12, 2015 at 19:11
  • 23
    @Pierre-LucPineault : is it not dangerous to store it as an int? Wouldn't all someone have to do is re-order the enum and then immediately all the values in your database is pointing to the wrong enum without any warning Commented Jun 23, 2016 at 6:13
  • 1
    @Diskdrive You can assign a specific integer to your enum so even when reordered it doesn't change (And often with powers of 2 so you can declare it as a 'Flag'). But usually you just don't go reordering enums for fun. Commented Jun 23, 2016 at 13:26
  • 46
    @Pierre-LucPineault, what Diskdrive is referring to is a real problem. Enums can get arranged for reasons other than fun, both validly and accidentally. It's a more robust solution to store the strings; those don't change. But if they do, no data is lost, just a db update is needed. Plus, looking at the table that way is more productive, and there's no need to number the values, which is nice on non-flag enums. In fact, this is how xml serialization works. So, at least some MS developers agree with Diskdrive. Commented Feb 25, 2017 at 0:42

9 Answers 9

89

In Entity Framework Core you can specify the built-in conversion.

If you have an enum type

public enum MyEnumType
{
    ...
}

and a model class with this property

public class EntityWithEnum
{
    public MyEnumType MyEnum { get; set; }
    ...
}

then you can add the built-in conversion

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder
        .Entity<EntityWithEnum>()
        .Property(d => d.MyEnum)
        .HasConversion(new EnumToStringConverter<MyEnumType>());
}

More details here.

UPDATE: as mentioned in the comments, there is an even more concise notation that achieves the same using the .HasConversion<string>(); method:

modelBuilder
    .Entity<EntityWithEnum>()
    .Property(d => d.MyEnum)
    .HasConversion<string>();
Sign up to request clarification or add additional context in comments.

7 Comments

This is only available in EF core.
I have a .NET Core 3.1 and I get this build error when using HasConversion Error CS1061 'PrimitivePropertyConfiguration' does not contain a definition for 'HasConversion' and no accessible extension method 'HasConversion' accepting a first argument of type 'PrimitivePropertyConfiguration' could be found (are you missing a using directive or an assembly reference?)
use .HasConversion<string>();
Where does DataSetSemanticType come from? it doesn't seem to be a known type in .NET and it's neither defined in the question. Please provide an example, I don't understand how this class should look like
@JérômeMEVEL The answer has been modified to be more understandable.
|
43

You can save the enum to the db as a string, and I agree with dotctor that it is not the best idea, but if you need to, you need to make a few changes.

public class User
{
    public int ID { get; set; }
    public string Name { get; set; }
    public List<Wepon> WeposInList { get; set; }

    [Column("Type")]
    public string TypeString
    {
       get { return Type.ToString(); }
       private set { Type= value.ParseEnum<Type>(); }
    }

    [NotMapped]
    public Type Type { get; set; }
}  

Add this extension class to your project.

public static class StringExtensions
{
    public static T ParseEnum<T>(this string value)
    {
        return (T)Enum.Parse(typeof(T), value, true);
    }
}

Full details are here - http://NoDogmaBlog.bryanhogan.net/2014/11/saving-enums-as-strings-with-entity-framework/

4 Comments

how would I use this to track both the label and the id in the database? I'm getting a null argument exception when I fetch a record.
Try removing [NotMapped] and set it to some other column in the db
the only actual answer to the OP's question. :)
Wouldn't this violate SOLID? By doing so your entity would not only describe your data, it would describe business as well. What I do is to have an enum, which I would seed to the database, and then refer it from there.
28

Furthering to Martin's answer, Entity Framework Core has a pre-defined converter for enum to string.

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder
        .Entity<User>()
        .Property(e => e.Type)
        .HasConversion<string>();
}

The same can be achieved by:

public class User
{
    public int ID { get; set; }

    public string Name { get; set; }

    [Column(TypeName = "nvarchar(24)")]
    public Type Type { get; set; }

    public List<Weapon> WeaponsInList { get; set; }
}

Comments

14

I had this problem as far as I remember and honestly I don't know why didn't MS add this feature (NH can do it like since always..).

Any ways, what I usually did is use const strings classes like:

public static class MyEnum
{
    public const string Foo = "Foo";
    public const string Bar = "Bar";
}

public class Client
{

    public string MyVal { get; set; }

    public Client()
    {
        MyVal = MyEnum.Bar;
    }

}

Cons - as simple as can be.

Downsides - you loose type checking (though it could be enforced programmatically).


So this time I tried to think of something more ambitious. So I took the concept described by Brian (which has some downsides when e.g. a given enum is used widely across the domain). And well.. I got the following working:

A base component class to store the values:

[ComplexType]
public class DbEnum<TEnum>
{
    public string _ { get; set; }

    public DbEnum()
    {
        _ = default(TEnum).ToString();
    }

    protected DbEnum(TEnum value)
    {
        _ = value.ToString();
    }

    public TEnum ToEnum()
    {
        return _.ToEnum<TEnum>();
    }

    public static implicit operator DbEnum<TEnum>(TEnum value)
    {
        return new DbEnum<TEnum>(value);
    }

    public static implicit operator TEnum(DbEnum<TEnum> value)
    {
        return value.ToEnum();
    }
}

... which would be basically sufficient.. except EF doesn't support generic types...

This means for every enum you have to have something like...

public enum PrivacyLevel
{
    Public,
    Friends,
    Private
}

public class PrivacyLevelEnum : DbEnum<PrivacyLevel>
{
    public PrivacyLevelEnum() : this(default (PrivacyLevel))
    {      
    }

    public PrivacyLevelEnum(PrivacyLevel value) : base(value)
    {
    }

    public static implicit operator PrivacyLevelEnum(PrivacyLevel value)
    {
        return new PrivacyLevelEnum(value);
    }

    public static implicit operator PrivacyLevel(PrivacyLevelEnum value)
    {
        return value.ToEnum();
    }
}

Which gives you some boiler-plate that could be easily generated e.g. using T4 templates.

Which finally ends you up with using:

public class CalendarEntry : Entity
{

    public virtual PrivacyLevelEnum PrivacyLevel { get; set; } = new PrivacyLevelEnum();

}

But since you have implicit conversion in place, class declarations are the only ones to be aware of the helper types.

2 Comments

Where is this method found string.ToEnum<TEnum>(); - is it an extension method
@Ken yes - public static T ToEnum<T>(this string value) { return (T)Enum.Parse(typeof(T), value, true); }
9

Explicit enum to string conversions would make your code messy and you'd have to keep parsing values. The same goes for lookup tables. Just add [Column] attribute to your enum field and specify TypeName as nvarchar (for SQL) or varchar (for postgres). Worked for me like a charm. In your case, for example :

public class User
{
    public int ID { get; set; }

    public string Name { get; set; }

    [Column(TypeName = "nvarchar(20)")]
    public Type Type { get; set; }

    public List<Wepon> WeposInList { get; set; }

    }  

You can read more about it in the official documentation here

2 Comments

Owrked for me in EF Core. Nice. Especially note that it's downward compatible, i.e. after converting the db from int to string column type, the "integer strings" will still be parsed correctly.
This did not work without specifying an int<=>string converter in my context's OnModelCreating method.
2

It's not a good idea to store them as string but you can create look up tables for your enums with ef enum to lookup and it is very easy to use.

2 Comments

Can you please explain a bit why it is not a good idea to store enum as nvarchar()? Isn't text more readable than an integer in the database?
For example easier to keep enum even for sorting. Otherwise you need to create some maps, where in enum just change the number. Also depends on db, where on some, string performance is worst than int.
2
protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder)
{
  configurationBuilder.Properties<Enum>().HaveConversion<string>();
}

Place that code inside your database context class. Such solution will handle every enum that you have in your db entries.

https://learn.microsoft.com/en-us/dotnet/api/microsoft.entityframeworkcore.dbcontext.configureconventions?view=efcore-9.0

3 Comments

What is configurationBuilder and where can I get one?
Thank you for contributing to the Stack Overflow community. This may be a correct answer, but it’d be really useful to provide additional explanation of your code so developers can understand your reasoning. This is especially useful for new developers who aren’t as familiar with the syntax or struggling to understand the concepts. Would you kindly edit your answer to include additional details for the benefit of the community?
1

I thing, that it is much more useful to store them as int because you can then cast the int from DB very easily to the enum.

But if it what you desire, there are two approaches. You can save Type.Zombie.ToString() (or Type.Human.ToString() respectively) to database (which will be "Zombie"), or you can obtain the value of DescriptionAttribute, which you are using and save that to the DB. How to get the description is described here. - It will be also "Zombie" in this case, but it may be whatever else you write in the Description().

If you use ToString you can then use Enum.Parse to get the instance of the enum back. If you use the description, it is not that easy.

1 Comment

Entity Framework supports enums now and there is no need to cast the int from DB to it's enum respective value .
0

You can use Newtonsoft property

[JsonConverter(typeof(StringEnumConverter))]
public enum MyEnum
{
}

1 Comment

Entities are not converted to JSON when storing in a typical database, so this will only work if you try to serialize the entity to JSON, but no reading/writing from/to the database.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.