2

My database has string values that are constrained to a fixed set of values like:

REGISTERED
PENDING
BANNED

Currently I am using constants to represent this values, but I would rather use an enum.

I am using EF 6, which I believe supports enums, but can I use enumerations and have it save to the database as the string representation in upper case?

public enum RegistrationStatus
{
    REGISTERED,
    BANNED,
    PENDING
}
Corey Adler
  • 15,897
  • 18
  • 66
  • 80
loyalflow
  • 14,275
  • 27
  • 107
  • 168
  • How does it differ from your previous questions: [Is it possible to gaurantee the value of what ToString for an enum will be?](http://stackoverflow.com/questions/20620032/is-it-possible-to-gaurantee-the-value-of-what-tostring-for-an-enum-will-be) – MarcinJuraszek Dec 16 '13 at 21:29
  • The difference is that I'm not sure how Entity Framework will save the model, I am assuming it will want to store the integer value no? I have always used enumerations in my models and at the database level the field/column is an integer, not a string representation. – loyalflow Dec 16 '13 at 21:34
  • Out of curiosity, what's the difference in your project for saving it as a string vs. an integer? – Corey Adler Dec 16 '13 at 21:39
  • @IronMan84 I don't have control over the database, and it is storing it as a string. – loyalflow Dec 16 '13 at 22:36

2 Answers2

3

I apologise that this is going to be so much code but you can create a string based type safe enum and the only way to demonstrate it working is to provide a working example.

The class

public sealed class Frequency : IEnumeration, IComparable

Which we declare in our POCO as

public Frequency chargeFrequency { get; set; }

And in the map file something like this - note that we map to the value property of the Frequency class

this.Property(t => t.chargeFrequency.Value)
    .HasColumnName("chargeFrequency")
    .HasColumnType("varchar")
    .IsOptional()
    .HasMaxLength(25);

It works a treat.

x.chargeFrequency = Frequency.Annually

The only drawback is that intellisense can't figure it out for us.

The static portion of the class

private static readonly Dictionary<string, Frequency> _list = 
    new Dictionary<string, Frequency>();

public static bool Contains(string key)
{
    return _list.Any(x => x.Key == key);
}

public static bool operator !=(Frequency a, Frequency b)
{
    return (!a.Equals(b));
}

public static bool operator ==(Frequency a, Frequency b)
{
    return (a.Equals(b));
}

public static implicit operator Frequency(string value)
{
    if (value == null || value.Trim().Length == 0) return Frequency.Null;
    if (Contains(value))
        return _list[value];
    else
        throw new InvalidCastException(string.Format("Frequency.{0} not found.", value));
}

public static implicit operator String(Frequency item)
{
    return item.Value;
}

public static readonly Frequency Null = new Frequency("");
public static readonly Frequency Annually = new Frequency("Annually");
public static readonly Frequency Fortnightly = new Frequency("Fortnightly");
public static readonly Frequency FourWeekly = new Frequency("FourWeekly");
public static readonly Frequency HalfYearly = new Frequency("HalfYearly");
public static readonly Frequency Monthly = new Frequency("Monthly");
public static readonly Frequency Quarterly = new Frequency("Quarterly");

The instance portion of the class

private readonly string _value;
private readonly bool _readonly;
private Frequency _instance;

private Frequency(string value)
{
    _readonly = true;
    _value = value;
    _instance = this;
    _list[value] = _instance;
}

public Frequency()
{
    _readonly = false;
    _instance = Frequency.Null;
}

[XmlText]
public string Value
{
    get
    {
        if (_instance == Frequency.Null) return null;
        return _instance._value;
    }
    set
    {
        if (_readonly)
            throw new InvalidOperationException("Cannot assign a value to an enumeration.");
        if (value == null) _instance = Frequency.Null;
        else
        {
            if (!_list.ContainsKey(value))
                throw new NullReferenceException(string.Format("Frequency.{0} not found.", value));
            _instance = _list[value];
        }
    }
}

public override string ToString()
{
    return Value;
}

public override bool Equals(object obj)
{
    if (obj == null) return false;

    if (this.GetType() != obj.GetType()) return false;

    // safe because of the GetType check
    Frequency item = (Frequency)obj;

    return System.Object.ReferenceEquals(_instance, item);
}

public override int GetHashCode()
{
    if (_value == null) return "".GetHashCode();
    else return _value.GetHashCode();
}

public int CompareTo(object obj)
{
    if (obj is Frequency)
    {
        if (_value == null)
        {
            if (obj == null) return 0;
            else return -1;
        }
        Frequency compare = (Frequency)obj;
        return _instance.Value.CompareTo(compare.Value);
    }
    else
        throw new ArgumentException("Object is not a Frequency.");
}
qujck
  • 14,388
  • 4
  • 45
  • 74
0

Based on your response to my comment, here is my answer:

EF has fully supported using enums to map to database fields since EF 5, but will only do it for int values. Considering that your database uses a string, I would suggest that you do some "indirect mapping". What I mean is to have a NotMapped field on your object (of that enum type) that will populate the actual database field with the string when the enum is changed. Something like this:

public string RegistrationStatus { get; set; }

[NotMapped]
public RegistrationStatus EnumStatus
{
  get { return (RegistrationStatus) Enum.Parse(typeof(RegistrationStatus), this.RegistrationStatus); }
  set { this.RegistrationStatus = value.ToString();
}

By doing this you can still map properties on the page to this field and know that when it changes it will change the underlying string value accordingly.

Corey Adler
  • 15,897
  • 18
  • 66
  • 80