简体   繁体   中英

Map string column in Entity Framework to Enum

Is there a way to map a string column to an enum in an Entity Model?

I have done this in Hibernate, but can't figure it out in EMF.

Probably a nicer version.

OrderStateIdentifier field is used for both JSON serialization and database field, while OrderState is only used in the code for convenience.

    public string OrderStateIdentifier
    {
        get { return OrderState.ToString(); }
        set { OrderState = value.ToEnum<OrderState>(); }
    }

    [NotMapped]
    [JsonIgnore]
    public OrderState OrderState { get; set; }


public static class EnumHelper
{
    /// <summary>
    /// Converts string to enum value (opposite to Enum.ToString()).
    /// </summary>
    /// <typeparam name="T">Type of the enum to convert the string into.</typeparam>
    /// <param name="s">string to convert to enum value.</param>
    public static T ToEnum<T>(this string s) where T: struct
    {
        T newValue;
        return Enum.TryParse(s, out newValue) ? newValue : default(T);
    }
}

It is ugly, but for mapping enums to strings I found something like this:

public virtual string StatusString
{
    get { return Status.ToString(); }
    set { OrderStatus newValue; 
          if (Enum.TryParse(value, out newValue))
          { Status = newValue; }
        }
}

public virtual OrderStatus Status { get; set; } 

OrderStatus is the enumerator type, Status is the enumerator and StatusString is the string version of it.

Acutally I think there is another solution to this.

What we did in our Project recently was using Extension Methods.

I wrote two of them, one for the Enum and one for the Entity, but here is the Example:

namespace Foo.Enums
{
    [DataContract]
    public enum EAccountStatus
    { 
        [DataMember]
        Online,
        [DataMember]
        Offline,
        [DataMember]
        Pending
    }

... the enum itself, and now the extension methods containing static class:

    public static class AccountStatusExtensionMethods
    {

        /// <summary>
        /// Returns the Type as enumeration for the db entity
        /// </summary>
        /// <param name="entity">Entity for which to check the type</param>
        /// <returns>enum that represents the type</returns>
        public static EAccountStatus GetAccountStatus(this Account entity)
        {
            if (entity.AccountStatus.Equals(EAccountStatus.Offline))
            {
                return EAccountStatus.Offline;
            }
            else if (entity.AccountStatus.Equals(EAccountStatus.Online))
            {
                return EAccountStatus.Online;
            }
            else if (entity.AccountStatus.Equals(EAccountStatus.Pending))
            {
                return EAccountStatus.Pending;
            }
            throw new System.Data.Entity.Validation.DbEntityValidationException(
                "Unrecognized AccountStatus was set, this is FATAL!");
        }

... the extension method for the entity type, and a convenience method for shorter typing:

        /// <summary>
        /// Gets the String representation for this enums choosen 
        /// </summary>
        /// <param name="e">Instance of the enum chosen</param>
        /// <returns>Name of the chosen enum in String representation</returns>
        public static String GetName(this EAccountStatus e)
        {
            return Enum.GetName(typeof(EAccountStatus), e);
        }
    }
}

... and finally usage:

// to set always the same, mappable strings:
db.AccountSet.Single(m => m.Id == 1).Status = EAccountStatus.Online.GetName();

// to get the enum from the actual Entity you see:
EAccountStatus actualStatus = db.AccountSet.Single(m => m.Id == 1).GetAccountStatus();

Now, you just need to be "using Foo.Enums;" and you can call the methods on the entity as well as on the enum. And even better, in some sort of wrappers for your entities, you could also do seamless marshalling between defferent types representing the same thing in big projects.

The only thing worth noting about this is that you sometimes have to execute the extension method before you hand your Linq expression to Linq. The problem here is that Linq can't execute the extension method in its own context...

Maybe just an alternative, but we've done it like that because it gives you great flexibility on how to get things for entities. You could easily write an extension to receive an Accounts actual Products in ShoppingCart...

Greetings, Kjellski

An alternative is to use a static class with string const fields instead of enums.

For example:

public class PocoEntity
{
    public string Status { get; set; }
}

public static class PocoEntityStatus
{
    public const string Ok = "ok";
    public const string Failed = "failed";
}

For added validation on the database side you can add a check constraint to verify that the column is the expected value (you can do this when mapping to an enum as well, but since the property is just a string this helps ensure the consumer of your api set the value properly).

ALTER TABLE [PocoEntity]
    ADD CONSTRAINT [CHK_PocoEntity_Status]
    CHECK ([Status] in ('ok', 'failed'));

You can do either:

Decorate the Enum property in your class as a text column

[Column(TypeName = "nvarchar(50)")]
public FileTypes FileType { get; set; }

OR

in your DatabaseContext class, override the OnModelCreating and add:

modelBuilder
  .Entity<File>()
  .Property(e => e.FileType)
  .HasConversion(new EnumToStringConverter<FileTypes>());

If you want to map the enum value to another correspondent string (ex. abbreviation) you can use this approach:

public class MinhaClasse
{
    public string CodTipoCampo { get; set; }

    [NotMapped]
    public TipoDado TipoCampo
    {
        get => DictValorTipoDado.SingleOrDefault(e => e.Value == CodTipoCampo).Key;
        set => CodTipoCampo = DictValorTipoDado[value];
    }

    private Dictionary<TipoDado, string> DictValorTipoDado = new Dictionary<TipoDado, string>()
    {
        { TipoDado.Texto, "T" },
        { TipoDado.Numerico, "N" },
        { TipoDado.Data, "D" }
    };

    public enum TipoDado { Texto, Numero, Data }
}

I had the same problem. I've come up with a solution, but I'm not completely happy with it.

My Person class has a Gender enum, and I use data annotations to map the string to the database and ignore the enum.

public class Person
{
    public int PersonID { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }

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

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

here is the extension method to get the correct enum from the string.

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

I wrote a blog post about this - http://nodogmablog.bryanhogan.net/2014/11/saving-enums-as-strings-with-entity-framework/

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM