Search code examples
c#entity-framework-coreef-core-8.0

System.InvalidOperationException: Cannot convert string value 'i' from the database to any value in the mapped 'ItemType' enum


I want to use an enum in my model, but in the database its stored as the Description attribute value, how can I make EF core understand this?

public enum ItemType
{
    [Description("s")]
    Floor,
    
    [Description("i")]
    Wall
}

Model:

public class Item
{
    public int Id { get; set; }
    public ItemType Type { get; set; }
}

Mapping

modelBuilder.Entity<ItemDto>()
    .Property(e => e.Type)
    .HasConversion<string>();

Solution

  • As discussed in the comment, EF Core doesn't know how to convert/mapping the Enum value based on the DescriptionAttribute. You need to implement a Value Converter to perform the mapping.

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        var enumDescriptionMappingConverter = new ValueConverter<ItemType, string>(
            v => v.ToDescription(), 
            v => EnumExtensions.GetValueByDescriptionAttribute<ItemType>(v));
    
            modelBuilder.Entity<Model>()
                .Property(e => e.Type)
                .HasConversion(enumDescriptionMappingConverter);
    }
    

    The first argument in ValueConverter is which converts the Enum value to a string based on DescriptionAttribute (From model to DB).

    The second argument in ValueConverter is which converts the stored DB (string) value to the Enum (From DB to model).

    While, I implement the extension/helper methods that are necessary in your scenario.

    public static class EnumExtensions
    {
        public static string ToDescription(this Enum value)
        {
            var attribute = value.GetAttribute<DescriptionAttribute>();
            if (attribute == null)
                return value.ToString();
    
            return attribute.Description;
        }
    
        public static TEnum GetValueByDescriptionAttribute<TEnum>(string value)
            where TEnum : Enum
        {
            var enumDict = Enum.GetValues(typeof(TEnum))
                .Cast<TEnum>()
                .ToDictionary(k => k, v => v.ToDescription());
    
            return enumDict
                .Single(x => x.Value == value)
                .Key;
        }
    
        private static T GetAttribute<T>(this Enum value)
            where T : Attribute
        {
            Type type = value.GetType();
            MemberInfo[] memberInfo = type.GetMember(value.ToString());
            var attributes = memberInfo[0].GetCustomAttributes(typeof(T), false);
    
            return (T)attributes.FirstOrDefault();
        }
    }