Entity Framework 4 Enum support in Linq

As many of you might know, Entity Framework 4 still lacks support to map enum properties.
There are countless of more or less worthless workarounds, everything from exposing constants integers in a static class to make it look like an enum to totally insane generics tricks with operator overloading.

None of those are good enough IMO, I want to be able to expose real enum properties and make Linq queries against those properties, so I’ve decided to fix the problem myself.

My approach will be using Linq Expression Tree rewriting using the ExpressionVisitor that now ships with .net 4.
By using the ExpressionVisitor I can now clone an entire expression tree and replace any node in that tree that represents a comparison between a property and an enum value.

In order to make this work, the entities still needs to have an O/R mapped integer property, so I will rewrite the query from using the enum property and enum constant to use the mapped integer property and a constant integer value.

For me this solution is good enough, I can make the integer property private and make it invisible from the outside.

Example

public class Order
{
     //this is the backing integer property that is mapped to the database
  private int eOrderStatus {get;set;}

  //this is our unmapped enum property
  public OrderStatus Status
     {
  get{return (OrderStatus) eOrderStatus;}
            set{eOrderStatus = (int)value;}
     }

     .....other code
}

This code is sort of iffy and it does violate some POCO principles but it is still plain code, nothing magic about it..

So how do we get our linq queries to translate from the enum property to the integer property?

The solution is far simpler that I first thought, using the new ExpressionVisitor base class I can use the following code to make it all work:

namespace Alsing.Data.EntityFrameworkExtensions
{
    public static class ObjectSetEnumExtensions
    {
        private static readonly EnumRewriterVisitor visitor = new EnumRewriterVisitor();
        private static Expression< Func< T, bool>> ReWrite< T>(this Expression< Func< T, bool>> predicate)
        {
            var result = visitor.Modify(predicate) as Expression< Func< T, bool>>;
            return result;
        }

        public static IQueryable< T> Where< T>(this IQueryable< T> self,
            Expression< Func< T, bool>> predicate) where T : class
        {
            return Queryable.Where(self, predicate.ReWrite());
        }

        public static T First< T>(this IQueryable< T> self,
            Expression< Func< T, bool>> predicate) where T : class
        {
            return Queryable.First(self, predicate.ReWrite());
        }
    }

    public class EnumRewriterVisitor : ExpressionVisitor
    {
        public Expression Modify(Expression expression)
        {
            return Visit(expression);
        }

        protected override Expression VisitUnary(UnaryExpression node)
        {
            if (node.NodeType == ExpressionType.Convert && node.Operand.Type.IsEnum)
                return Visit(node.Operand);

            return base.VisitUnary(node);
        }

        protected override Expression VisitMember(MemberExpression node)
        {
            if (node.Type.IsEnum)
            {
                var newName = "e" + node.Member.Name;
                var backingIntegerProperty = node.Expression.Type.GetMember(newName, System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Public)
                    .FirstOrDefault();

                return Expression.MakeMemberAccess(node.Expression, backingIntegerProperty);
            }

            return base.VisitMember(node);
        }
    }
}

The first class, is an extension method class that overwrite the default “where” extension of IQueryable of T.
The second class is the actual Linq Expression rewriter.

By including this and adding the appropriate using clause to your code, you can now make queries like this:

var cancelledOrders = myContainer.Orders.Where(order => order.Status == OrderStatus.Cancelled).ToList();

You can of course make more complex where clauses than that since all other functionality remains the same.

This is all for now, I will make a followup on how to wrap this up in a Linq query provider so you can use the standard linq query syntax also.

Hope this helps.

//Roger

8 Comments

  1. Mike Chaliy says:

    var cancelledOrder = myContainer.Orders.First(order => order.Status == OrderStatus.Cancelled);

    Seems this will not work…?

    1. Roger Alsing says:

      I’ve updated the extension method class to include the “First(predicate)” method.
      There are other extensions that also use predicates that are still left out of this sample…

  2. Dresel says:

    Hi,

    i think your idea is great, i’m trying to implement something similar for Entity comparison. Im currently testing and implementing your solution, but i’m stuck when nested queries comes into play (im using a sample model from the oreally programming ef4 book):

    var context = new PROGRAMMINGEFDB1Entities();

    var universe = from oa in context.Addresses
    where oa.StateProvince == “Ontario”
    select oa;

    var results = (from oa in universe
    select new
    {
    oa,
    contact = (from c in context.Contacts
    where c.ContactID == oa.ContactID
    select c)
    }).ToList();

    This will fail with the message
    “LINQ to Entities does not recognize the method ‘System.Linq.IQueryable`1[ConsoleApplication3.Contact] Where[Contact](System.Linq.IQueryable`1[ConsoleApplication3.Contact], System.Linq.Expressions.Expression`1[System.Func`2[ConsoleApplication3.Contact,System.Boolean]])’ and this method cannot be translated into a store expression.

    Do I have to implement another Where() function?

    Greetz,
    Dresel

  3. Justin says:

    Great aticle, I like what you propesed.

    Question for you, what if the database stores the enum value as a string? Is it possible to replace the referenced enum value with its corresonding string value in addition to swapping the property referenced in the expression?

  4. Justin says:

    Btw, that first sentence should read: “Great article, I like what you proposed.” I’m nothing without spell checker.

  5. Vlad says:

    Hi Roger,
    Great article. I’m trying to implement the extension method, but am getting the error “The call is ambiguous between the following methods or properties ObjectSetEnumExtensions.Where and System.Linq.Queryable.Where”.. What am I missing? How do I override the default where extension?

  6. Shimmy says:

    Hi Roger and thanks for this post.
    I’m encountering the same issue, I am trying to use proxy properties as you suggested, my problem is, that in my case it’s a Silverlight RIA project, and once I mark the generated numeric property (i.e. the database column) as private, it’s not generated in the client, and thus, the proxy property is now invalid and won’t compile on the client.
    I can show some code snippets if I’m unclear.

    Any suggestions will be really appreciated!

Leave a Comment