More Entity Framework frustration [I want my Specification Pattern].
A while back I posted about LINQ To SQL and how to implement the specification pattern and fetching strategies with a IRepository. Since then you may have heard the announcements and followed the ensuing debate about the future of LINQ To SQL. On reflection I have since decided to explore EF as fully as possible and prepare for knowing it deeply, particularly as it promises to embody values such as support of POCO and Lazy Loading which are some of things I am not prepared to live without.
In the meantime I thought it was worth taking the same approach as I had with LINQ To SQL and see what was possible with respect to implementing the Specification Pattern in order to provide dynamic querying ability (minus the repository - for the moment only).
Let’s start with a given Entity Model:
I am not going to use much of this model for the purpose of this post but some time soon I will look at how to implement a Fetching Strategies with Entity Framework in a similar manner as that which I demonstrated with LINQ To SQL, but let’s move forward with the specification patterns for now.
So given my model has Orders which aggregate Order_Details and Orders may contain discontinued products I might hypothetically want to sum the total of my unfulfilled problem orders where they contain discontinued products. If I just whip out my specification library (built for LINQ’ish entity / object matching and LINQ To SQL dynamic querying) and I attempt to use it directly with the Entity Framework in this fashion, I get the same behaviour as I expected previously with the other technologies.
static void Main(string[] args) { Specification<Order> order_spec = new Specification<Order>(o => o.Order_Details.All (od => od.Product is DiscontinuedProduct)); using (var ctx = new NorthwindEntities()) { ObjectQuery<Order> orders = (ObjectQuery<Order>) from o in ctx.Orders .Include(“Order_Details”) .Include(“Customer”) .Where(order_spec.EvalPredicate) select o; Console.WriteLine(orders.ToTraceString()); try { foreach (var order in orders) { var sumup = order.Order_Details.Sum (od => od.Quantity * od.UnitPrice); Console.WriteLine(sumup.ToString()); } } catch (ArgumentException argEx) { Console.WriteLine(argEx.Message); } } Console.ReadLine(); }
Ok, that’s great but now I want to try combining specifications which I can do by taking advantage of the operator overloading for OR and AND. Bam, crash it comes tumbling down.
Now its late and I am starting to get annoyed with the Entity Framework. As it turns out (as far as I can tell), LINQ to SQL is far more pliable in managing dynamic queries. Why? Well EF doesn’t play nice with Expression<T>, specifically Expression<Func<T>> etc. Errors arise when you build and invoke Expressions because the Entity Framework doesn’t support Expression.Invoke which is required to combine Expressions.
private class OrSpecification : Specification<T> { private readonly ISpecification<T> left; private readonly ISpecification<T> right; public OrSpecification(ISpecification<T> left, ISpecification<T> right) { this.left = left; this.right = right; this._evalFunc = (Func<T, bool>)Func<T, bool>.Combine (left.EvalPredicate.Compile(), right.EvalPredicate.Compile()); ParameterExpression parameter = Expression.Parameter(typeof(T), “p”); var invokedExpression = Expression.Invoke(left.EvalPredicate, right.EvalPredicate.Parameters.Cast<Expression>()); _evalPredicate = Expression.Lambda<Func<T, bool>> (Expression.Or(right.EvalPredicate.Body, invokedExpression), right.EvalPredicate.Parameters); } public override bool Matches(T entity) { return EvalPredicate.Compile().Invoke(entity); } }
private static void CombinedOrSpecExample() { Specification<Order> order_spec = new Specification<Order>(o => o.Order_Details.All (od => od.Product is DiscontinuedProduct)); Specification<Order> german_customer_spec = new Specification<Order>(c => c.Customer.Country == “Germany”); var orSpec = order_spec | german_customer_spec; using (var ctx = new NorthwindEntities()) { var orders = (ObjectQuery<Order>) from o in ctx.Orders .Include(“Order_Details”) .Include(“Customer”) .Where(orSpec.EvalFunc).AsQueryable<Order>() select o; Console.WriteLine(orders.ToTraceString()); try { foreach (var order in orders) { var sumup = order.Order_Details.Sum (od => od.Quantity * od.UnitPrice); Console.WriteLine(“The sum of orders for order ID: {0} is {1}”, order.OrderID.ToString(), sumup.ToString()); } } catch (ArgumentException argEx) { Console.WriteLine(argEx.Message); } } }
This code produces a casting error:
7 Comments so far
Leave a reply









I faced a similar issue with EF in that I wanted to use the Predicate Builder (http://www.albahari.com/nutshell/predicatebuilder.aspx) and it failed on Expression.Invoke().
The following post has a potential solution to the problem:
http://stackoverflow.com/questions/110314/linq-to-entities-building-where-clauses-to-test-collections-within-a-many-to-ma#131551
[Reply]
Thanks Brad, I did have that page (and about two dozen others) saved overnight as browser tabs ready to reopen and look at when I woke this morning and your comment has directed me immediately to it saving me some very very valuable time. Going to give it shot now and once again, many thanks.
[Reply]
Brad, using the extracted ExpressionVisitor and accompanying support classes did provide a solution to the problem. I did find a couple of different posts http://blogs.msdn.com/mattwar/archive/2007/07/31/linq-building-an-iqueryable-provider-part-ii.aspx and http://blogs.msdn.com/meek/archive/2008/05/02/linq-to-entities-combining-predicates.aspx and one of them also listed the code for the ExpressionVisitor which negated my need of reflector.
Thanks
[Reply]
[...] I remarked that the Entity Framework did not support Expression.Invoke() which made combining Specifications to form .Or and .And Specifications in the current version of [...]
[...] Entity Framework, Repositories, Specifications and Fetching Strategies Part 1.0 [...]
I’ve updated LINQKit so that it automatically reduces invocation expressions:
http://www.albahari.com/nutshell/linqkit.aspx
This means that you can use PredicateBuilder with Entity Framework - simply by referencing LINQKit and adding AsExpandable() to the query:
var predicate = … // Build with PredicateBuilder
myObjectContext.Products.AsExpandable().Where (predicate);
Joe
[Reply]
Nice.
Thanks Joseph. Will be sure to give it a run.
[Reply]