Tip 23 – How to fake Enums in EF 4


As of right now Enums are not in EF4.


Now we will be listening to your feedback about Beta1, and making some adjustments, so you never know, but at the moment it doesn’t look like they will be supported.


Yesterday though I came up with a workaround that, while a bit of work, is pretty interesting.


Workaround


To get this working you need .NET 4.0 and you need to use POCO classes.

Imagine you have an Enum like this:


public enum Priority

   High,
   Medium,
   Low
}


First step is to create a ComplexType with just one property, something like this:


<ComplexType Name=”PriorityWrapper” >
          <Property Type=”Int32″ Name=”Value” Nullable=”false” />
</ComplexType>


Then if you want to have a property in an Entity that returns an Enum instead use the wrapping ComplexType.


As I said this only works in POCO. The reason is you need to do some interesting things in your PriorityWrapper complex type class:


public class PriorityWrapper
{
   private Priority _t; 
   public int Value {
      get {
         return (int) _t;
      }
      set {
        
_t = (Priority) value;
      }
   }
   public Priority EnumValue
   {
      get {
         return _t;
      }
      set {
         _t = value;
      }
   }
}


Notice it has a Value property of type int just like the ComplexType definition, but it also has a way to set and get the Priority too via the EnumValue property.


Now we have this class we can use it in our POCO entities, so for example imagine you have a Task entity:


public class Task
{
    public virtual int Id { get; set; }
    public virtual PriorityWrapper Priority { get; set; }
    public virtual string Title{ get; set;}
}


The next step is interesting, add some implicit conversion between PriorityWrapper and Priority:


public static implicit operator PriorityWrapper(Priority p) 
{
   return new PriorityWrapper { EnumValue = p };
}


public static implicit operator Priority(PriorityWrapper pw)
{
   if (pw == null) return Priority.High;
   else return pw.EnumValue;
}


With these implicit conversions in place you gain the illusion that the Priority property on the Task class is actually a Priority.

For example you can do this:


Task task  = new Task {
   Id = 5,
   Priority = Priority.High,
   Title = “Write Tip 23”
};


Rather than this needing to do this:

Task task  = new Task {
   Id = 5,
   Priority = new PriorityWrapper {EnumValue = Priority.High },
   Title = “Write Tip 23”
};

And this:


if (task.Priority == Priority.High)


Rather than this:


if (task.Priority.EnumValue == Priority.High)


But what about queries?


You can even use this enum in queries:


var highPriority =
            from task in ctx.Task
            where task.Priority.Value == (int) Priority.High
            select task;

Cool huh?


Now this is not as good as if we natively supported enums, but it is not that far off, especially from the perspective of someone programming against your entities.


Enjoy.


This is 23rd post in my ongoing series of Entity Framework Tips.

Comments (51)

  1. MikaelHenrixon says:

    GREAT! I did the exact same thing in a billing project where the int value didn’t say much of the status. Never though of doing it for the entity framework.

    Nice solution, that should shut the critics up for a while 🙂

  2. Ngoc says:

    Will this code supported in this version of EF?

    var products = from p in db.Products

                  select new Product

                  {

                      Id = n.Id,

                      Name = n.Name

                  };

    I tried it with VS2010 Beta1 and this exception still occured (same in the previous version of EF) while it is valid in LINQ To SQL:

    "The entity or complex type ‘Demo.Product’ cannot be constructed in a LINQ to Entities query."

  3. Alex D James says:

    Ngoc,

    That code isn’t supported but this is:

    var anonproducts =

          from p in db.Products

          select new {Id = n.Id, Name = n.Name};

    var products =

          from p in anonproducts.AsEnumerable<T>()

          select new Product {Id = p.Id, Name = p.Name};

    Alex

  4. MichaelGG says:

    EF doesn’t support enums? What? That’s… wierd. Even Linq-to-Sql does.

  5. keith patton says:

    add the support, how hard can it be!!

  6. Phil Smith says:

    is PriorityWrapper missing a constructor?

  7. Alex D James says:

    Phil,

    No, I’m using the default constructor everywhere, along with a Object Initializer syntax.

    I.e.

    new Customer {Name = "ACME"};

    rather than

    new Customer("ACME");

    Hope this helps

    Alex

  8. Alex D James says:

    @Keith and Michael,

    I hear you.

    Alex

  9. Phil Smith says:

    Many thanks. Got my EF 4.0 pseudo enums working now.

    Can’t see why Microsoft aren’t implementing Enum mapping with ability to switch map to be either the int index or the string value.

    I wanted to pass the string section through to the database so my code is a little different. If anyone is interested here’s my code for an organization type enum:

    public enum OrganizationTypeEnum

       {

           Company,

           Subsidiary,

           Division,

           Department,

           Team

       }

       public class OrganizationTypeEnumWrapper

       {

           private OrganizationTypeEnum _organizationType;

           public OrganizationTypeEnum OrganizationType

           {

               get { return _organizationType; }

               set { _organizationType = value; }

           }

           public string StringValue

           {

               get { return _organizationType.ToString(); }

               set { _organizationType = (OrganizationTypeEnum)Enum.Parse(typeof(OrganizationTypeEnum), StringValue, true); }

           }

           public static implicit operator OrganizationTypeEnumWrapper(OrganizationTypeEnum p)

           {

               return new OrganizationTypeEnumWrapper { OrganizationType = p };

           }

           public static implicit operator OrganizationTypeEnum(OrganizationTypeEnumWrapper pw)

           {

               if (pw == null) return OrganizationTypeEnum.Company;

               else return pw._organizationType;

           }

       }

    Does anyone know whether EF 4.0 supports multiple model diagrams? My database will have circa 200 tables and i do not want 1 diagram.

  10. Alex D James says:

    @Phil,

    Nice solution, I was meaning to talk about that too, but it’s good to see you worked it out!

    As for your question about multiple diagrams. Unfortunately the answer is no, there was talk of implementing something like that, but I think it got left on the cutting room floor, so to speak.

    Alex

  11. Phil Smith says:

    Any ideas why Microsoft have not implemented Enums. My guess is that because behind the scenes they are instantiated as structs derived from base System.Enum then they do not support inheritance and that the EF4.0 implementation of change tracking will therefore not work with them? (EF 4.0 creates derived proxy objects from your POCO classes). Then again I may be talking fluff?

    I wonder whether its possible to somehow use extension methods on string type to perform implicit casts?

  12. Alex D James says:

    Phil,

    No I don’t think there are any major technical issues. We don’t need to create proxies for your Enums, because they are simply a property of your POCO class.

    The only reason they aren’t in the product is we were focusing on other things, like POCO, Model First etc.

    There is still some time to react to your and other peoples feedback so you never know.

    As for you question about extension methods and implicit casts, not sure, it would be nice wouldn’t it

    Alex

  13. morten@mertner.com says:

    It has been a source of constant annoyance with EF that it does not support enums. I could live with it being limited to only support mapping to int columns, but all we’re talking about here is the ability to make a few casts. It’ll take you an hour to implement but waste a million hours for all the EF users if you don’t..

  14. Alex D James says:

    @Morten,

    I hear you. Unfortunately no piece of code takes an hour at Microsoft. Not that I’m making excuses.

    I really want to see this get in too.

    Alex

  15. Hi,

    Nice but too bad it’s not in EF4. We even got this running in EF1 with Int and Char support. Just give it a week and make EF a real professsional ORM 🙂

  16. Alex D James says:

    Roger,

    I hear you. I wish it was just a week. While this from Eric Lippert is tongue in cheek there is a big element of truth too:

    How many Microsoft employees does it take to change a lightbulb…

    http://blogs.msdn.com/ericlippert/archive/2003/10/28/53298.aspx

    • Alex
  17. MichaelGG says:

    Alex — yes, I understand how much work MS has to do to ship features. But MS is so far behind in this space in general. I was really hoping that EF4 might be something I could drop NHibernate for. But it doesn’t look that way :(. It’s not just this one feature, but it’s that if this one little tiny thing doesn’t work right, then I can only imagine there must be all sorts of things all over the place that won’t work right once we get into it.

    In fact, on a new project, we decided to simply not bother even looking at EF4 because of the high probability that we might run into problems at some point, even if we don’t know what the problems are now. With NHibernate, it’s a safe choice, and we know we’re not gonna hit anything too strange that we can’t quickly find a fix/workaround for.

  18. betterBeAnnonymousThisTime says:

    <i>The only reason they aren’t in the product is we were focusing on other things, like POCO, Model First etc.</i>

    ok, someone is either joking here or I am being a dumb EF user…

    either way, why all that feedback then?

    finger crossed for enums support in EF 4.0

    I appreciate your work Alex, but I’ve dealt with other commercial ORM companies and (believe it or not) any request was made during the first 24 hours /and/or week (depending on the problem to deliver it)

    I hope EF won’t have a FREE (you get what you payed for) moniker, because, strangely, Microsoft is on the right way

    in the history you aren’t remembered by the good things you’ve done but by the few bad you’ve deployed, don’t destroy its name

    thanks (and I am not trolling here)

  19. dbuckle says:

    Is there a similar way of getting this working in 3.5 SP1 or is this a 4.0 only trick?

    I’ve very new to EF and I’m trying to migrate from LINQ2SQL to EF and this is a real headache (and we can’t use 2010 as its not rtm afaik)

    thanks

  20. Dave says:

    You can tidy this example up with generics, a bit:

       public class EnumType<T>

       {

           private T enumType;

           public int Value

           {

               get { return (int)(object)enumType; }

               set { enumType = (T)(object)value; }

           }

           public T EnumValue

           {

               get { return enumType; }

               set { enumType = value; }

           }

           public static implicit operator EnumType<T>(T p)

           {

               return new EnumType<T>{ EnumValue = p };

           }

           public static implicit operator T(EnumType<T> pw)

           {

               if (pw == null) return default(T);

               else return pw.EnumValue;

           }

       }

       public enum wah

       {

           something,

           boo,

           legs

       }

       public class bum

       {

           public bum()

           {

               EnumType<wah> www = wah.legs;

           }

       }

  21. Alex D James says:

    @Dave,

    Unfortunately I suspect your example won’t work in Beta2 because of a limitation in the EF, that means properties have to be declared at the same level in the CLR type hierarchy as they do in the EDM. Which I think means that the EF won’t be able to map classes derived from your Generic Base class to the corresponding EDM ComplexType.

    We’ve made some changes post Beta2 that might fix the problem. Basically this sort of generic base type trick will work for Entities post Beta2, not sure about ComplexTypes though.

    Alex

  22. Steve says:

    How can we make this work with the "Code Only" approach where you don’t have the edmx file to define the complex type?

  23. Alex D James says:

    Well you need to register the wrapper as a ComplexType something like this:

    builder.ComplexType<PriorityWrapper>()

    Code-Only ‘should’ pick up the int / string properties but ignore the enum property. I haven’t tested this yet myself, but I think it should work.

    Keen to hear how you go.

    Cheers

    Alex

  24. Steve says:

    I tried using the ComplexType() method and it sort of worked.  The only way I could get it to work was if the actual database column name was in the form: <PropertyName>_Value (e.g., "Priority_Value").  I can’t figure a way to make it work if I want my DB column named to be called something else.

    Incidentally, I created a generic class to wrap this the same way Dave suggest above and it works fine (although suffers from the same problem where I can’t control the DB column name).  

    If you have any suggestions for how I can specify the DB column name, I’d appreciate it.

    Sorry the belabor the point everyone else has made on the comments above, but if Microsoft doesn’t simply support enums the way LINQ to SQL has done for a number of years, it will be quite disappointing.

  25. Alex D James says:

    @Steve,

    To specify the column name you need to specify a mapping explicitly.

    So for example if you have this:

    public class Task{

     public int ID {get;set;}

     public string Name {get;set;}

     …

     public PriorityWrapper Priority {get;set;}

    }

    You would need something like this:

    builder.ComplexType<PriorityWrapper>();

    builder.MapSingleType<Task>(

      t => new {

         id = t.ID,

         name = t.Name,

         …

         priority = t.Priority.Value

      }

    );

    As you see you simply ‘.’ through to the property of the ComplexType you want to map.

    Re: not supporting Enum’s properly – I absolutely agree – it is a real shame.

    Hope this helps

    Alex

  26. Steve says:

    Alex – Thanks for your response.  It looks like this sort of worked.  A couple of interesting points that I’m observing:

    • If I want to use enums than this MapSingleType() appears to be the only way to make it work with the code only approach which means I have to map out every property of my type just to get it to see the enum.  Without the enum, I can just rely on convention and save myself all of that code.  So not incredibly user friendly.
    • When I switched to using this, the generic Wrapper<T> no longer worked.  I had to switch back to the non-generic wrapper which means it’s not a very re-usable situation (and hence, not very developer-friendly) for enums.

    • When I don’t use the MapSingleType() method, I can’t control the column name (unfortunate) but I’m able to use the generic Wrapper<T> which make the lack of enum support slightly more palatable.  But in the end, I can’t use that column name so my only choice would be to use MapSingleType().

    Again, thx for your responses.

  27. Shawn says:

    Is this workaround still possible with the final release of EF4?  I keep getting an error: "The Type 'xx.PriorityWrapper' already contains a definition for 'Value'"  Please let me know… I'm pulling my hair out trying to get this to work.  Thanks.

  28. dMarzo says:

    @Steve

    Could you paste your code first mapping for Wrapper<T>

    builder.ComplexType<Wrapper<wah>>()

      .Property(a => a.Value);

    EF 4.0 release say that "The Type 'EnumType`1[wah]' is not a valid complex type. Generic types are not supported.

    Thanks

  29. How do you write queries when storing the enum value as a string. I have tried:

    entities.Where(x => x.Status.Value == StatusEnum.Future.ToString());

    but get the following exception:

    LINQ to Entities does not recognize the method 'System.String ToString()' method, and this method cannot be translated into a store expression.

  30. Scott says:

    Is this still the best approach for Enums?  I am just getting started and quickly ran into this.

  31. Scott says:

    Is this still the best approach for Enums?  I am just getting started and quickly ran into this.

  32. Alex D James says:

    @Daniel

    You need to get the string before you use it:

    string enumValue = StatusEnum.Future.ToString();

    entities.Where(x => x.Status.Value == enumValue);

    @Scott

    Not sure if this is the 'Best' approach – all approaches have tradeoffs – it is however one we know works.

  33. Anthony Main says:

    I've been trying to implementing this but am not sure where I create the public class PriorityWrapper as the codegen already creates a partial one and therefore I get an abiguity conflict

  34. Martin says:

    Please make this work. Its absolutly a must.

    I like what i am seeing with the code first and convention based mapping/configuration just like Fluent NHibernate, but i wont change if something as simple as mapping to an enum doesnt work.

  35. splitpete says:

    What modifications are needed to make this work in the final release (as the post was written for beta)? PriorityWrapper must be in a different namespace since it is not marked as partial.

    Is that supposed to be the case? If so , the assigning and linq statements will not work as posted.

    If PriorityWrapper is supposed to be in the same namespace, when it is marked as partial the Value property is defined in two places.

    I feel I'm missing something very important or this example does not apply in its current from to the current EF release.

    Any thoughts?

  36. Dennis says:

    @Anthony Main

    This sample is for POCO only. So, default code generator must be turned off.

  37. Dennis.Petrov says:

    @Anthony Main

    This sample is for POCO only. So, default code generation must be turned off.

  38. Ivan says:

    This approach doesn't work for self-tracking entities as well.

  39. Rybolt says:

    @Dennis and author,

    Yes, sample is for POCO, but it doesn't seem to work to well with POCO Entity templates.  My problem, the complex type the author says to create is generated by the T4 templates, especially the property ("Value"), in which we need to add implementation too (per author).  Thus, how do we add our special wrapper implementation for our complex type, when the POCO Entity T4 templates will overwrite it ?

    Do we have to define the complex type by using 'code-first', b/c I am missing something trying 'design-first' with T4 POCO Entity templates ?

    Please help/

  40. carecki says:

    @Daniel, @AlexJ

    You don't need to get the string before you use it. Just add third implicit conversion – to string, like this:

    public static implicit operator string(OrganizationTypeEnumWrapper pw)
    
    {
    
        return pw == null ? null : pw.StringValue;
    
    }
    

    and you can use it like this:

    entities.Where(x =&gt; x.Status.Value == (OrganizationTypeEnumWrapper)enumValue);
    

    It works by immediately converting enumValue to OrganizationTypeEnumWrapper, and later, where strings are to be compared, converts it to string by the implicit operator newly added.

    The minus of this is casting, but it's like casting to (int) in original solution, and far way better than getting string before creating query.

  41. Darren Neimke says:

    A simpler version:

    public class Participant

    {

       public int ParticipantId { get; set; }

       public string FirstName { get; set; }

       public string LastName { get; set; }

       public string Username { get; set; }

       [Column(Name="Gender")]

       public int InternalGender { get; set; }

       [NotMapped]

       public Gender Gender

       {

           get { return (Gender)this.InternalGender; }

           set { this.InternalGender = (int)value; }

       }

       public DateTime DateOfBirth { get; set; }

    }

  42. Vijaya Anand says:

    Hi Alex,

    Thanks for your information.

    Everyone likes to keep their business model neat i.e. it should not be tied with a particular database framework(EF, NHihernate…). Making all the properties virtual or doing workarounds for using enums makes the model more tied with entity framework.

    What happens tomorrow the ADO.NET team comes with more new advanced framework. How I can use my current business model in that case. Do you expect me to remove all the virtual keywords from my properties and other nasty things.

  43. Vijaya Anand says:

    Hi Alex,

    Thanks for your information.

    Everyone likes to keep their business model neat i.e. it should not be tied with a particular database framework(EF, NHihernate…). Making all the properties virtual or doing workarounds for using enums makes the model more tied with entity framework.

    What happens if the ADO.NET team comes with a new advanced framework tomorrow. How I can use my business model in that case? Do I need to remove all the virtual keywords from the properties and other nasty things??

  44. DNG says:

    @Darren

    I couldn't get your example to work.The property  

    ' public Gender Gender' won't compile…

    Got any suggestions?

  45. Arnab says:

    Hi Alex,

    As of today, is Enum supported in EF for .NET 4(I mean VS2010)? I could'nt find clear documentation for this. Can you help with a small example in case it's available?

    Thanks

  46. Is there an example project I can download from you Alex? I've been trying hard to get this thing to work and feel rather stupid in the process. I can't get it to work with WCF. When I set my wrapper object to the enum value it says I can't implicitly convert. Here is my wrapper:

       public partial class AddressTypeWrapper

       {

           #region Primitive Properties

           private AddressType _a;

           public int Value

           {

               get

               {

                   return (int)_a;

               }

               set

               {

                   _a = (AddressType)value;

               }

           }

           public AddressType EnumValue

           {

               get

               {

                   return _a;

               }

               set

               {

                   _a = value;

               }

           }

           public static implicit operator AddressTypeWrapper(AddressType addressType)

           {

               return new AddressTypeWrapper { EnumValue = addressType };

           }

           public static implicit operator AddressType(AddressTypeWrapper addressTypeWrapper)

           {

               return addressTypeWrapper.EnumValue;

           }

           #endregion

       }

    I changed my entity property to be the wrapper type:

        [DataMember]

           public AddressTypeWrapper AddressTypeID

           {

          get;

        set;

             }

  47. Adam Moss says:

    The above is good, but it could be cleaned up a bit with generics and a little black magic, if you want to reuse the Value and EnumValue properties (can't do anything with the conversion operators).  And maybe someone could suggest better black magic for my EnumValue property?

    namespace DataTest.Model

    {

    public abstract class EnumWrapper&lt;EnumType&gt;
    
    {
    
        public int Value { get; set; }
    
        public EnumType EnumValue
    
        {
    
            get { return (EnumType) Enum.ToObject(typeof(EnumType), this.Value); }
    
            set { this.Value = (int) Enum.Parse(typeof(EnumType), Enum.GetName(typeof(EnumType), value)); }
    
        }
    
    }
    
    public class LogTypeWrapper : EnumWrapper&lt;LogType&gt;
    
    {
    
        public static implicit operator LogTypeWrapper(LogType enumValue)
    
        {
    
            return new LogTypeWrapper { EnumValue = enumValue };
    
        }
    
        public static implicit operator LogType(LogTypeWrapper wrapper)
    
        {
    
            return (wrapper == null) ? default(LogType) : wrapper.EnumValue;
    
        }
    
    }
    

    }

    By the way, Mr. Microsoft, we sure would like Enum type parameter constraints (seriously, why can't I do this:

    [where EnumType : Enum]?  Why is that explicitly forbidden?

  48. Adam Moss says:

    Oops…I posted old version. This better:

    public abstract class EnumWrapper&lt;EnumType, UnderlyingType&gt;
    
    {
    
        public UnderlyingType Value { get; set; }
    
        public EnumType EnumValue
    
        {
    
            get { return (EnumType) Enum.ToObject(typeof(EnumType), this.Value); }
    
            set { this.Value = (UnderlyingType) Enum.Parse(typeof(EnumType), Enum.GetName(typeof(EnumType), value)); }
    
        }
    
    }
    
    public class LogTypeWrapper : EnumWrapper&lt;LogType, int&gt;
    
    {
    
        public static implicit operator LogTypeWrapper(LogType enumValue)
    
        {
    
            return new LogTypeWrapper { EnumValue = enumValue };
    
        }
    
        public static implicit operator LogType(LogTypeWrapper wrapper)
    
        {
    
            return (wrapper == null) ? default(LogType) : wrapper.EnumValue;
    
        }
    
    }
    
  49. DZ says:

    All this code to make EF support enums ? No, thanks.

  50. weitzhandler says:

    Is there a way of enum support via entity designer (db first) via generated DbContext entities?

Skip to main content