views:

63

answers:

2

I would like to create a compiled query which uses reusable where predicates. An example to make this clear:

ObjectContext.Employees.Where(EmployeePredicates.CustomerPredicate)

EmployeePredicates is a static class with a property CustomerPredicate that looks like this:

public static Expression<Func<Employee, bool>> CustomerPredicate
    {
        get
        {
            return t => t.CustomerId == 1;
        }
    }

This works as expected. In most cases however, you would like to pass a parameter to Expression. To achieve this I have to change the property to a static function:

 public static Expression<Func<Employee, bool>> CustomerPredicate(int id)
    {
         return t => t.CustomerId == id;
    }

And I can use this like this:

ObjectContext.Employees.Where(EmployeePredicates.CustomerPredicate(id))

This works, but now comes the tricky part. I would like to compile this query... Visual studio doesn't give me any compile errors, but when I run this example the following exception is thrown at runtime: Internal .NET Framework Data Provider error 1025

Just so we're on the same page here is the full code that gives me the exception:

var _compiledQuery = CompiledQuery.Compile<AdventureWorksEntities, int, IQueryable<Employee>>(
                    (ctx, id) =>
                            (ctx.Employee.Where(EmployeePredicates.CustomerPredicate(id))
                        ));

Does anyone have a clue why this exception is being thrown? I've taken this way of working from http://www.albahari.com/nutshell/linqkit.aspx. Any help would be much appreciated.

A: 

The problem is that the Entity Framework is trying to examine the expression tree represented by

 (ctx, id) => (ctx.Employee.Where(EmployeePredicates.CustomerPredicate(id))

It can't do that, because it doesn't know what EmployeePredicates.CustomerPredicate does.

As for the best fix... I'm not sure. Basically it's got to know at query compile time what the full query looks like, just with the placeholders for parameters.

I suspect the best solution will involve something like this:

public static Expression<Func<Employee, int, bool>> CustomerPredicate()
{
     return (t, id) => t.CustomerId == id;
}

... as that raises the abstraction by one level; it gives you an expression tree which uses id as a ParameterExpression, which is something you'll need in order to build the appropriate expression tree to call CompileQuery. It gets a little hard to think about, unfortunately :(

Jon Skeet
A: 

Thanks Jon,

The problem with the approach you describe, is that chaining predicates together will become very hard. What if I need to combine this predicate with another predicate that filters the employees with a certain name? Then you end up with a lot of selects to pass in the parameters.

    (ctx, id, name) => 
(ctx.Employee.Select(emp => new {emp, id})
.Where(EmployeePredicates.CustomerPredicate(id))
.Select(emp => new {emp, name})
.Where(EmployeePredicates.NamePredicate(name))

It gets even worse when you're working on joined tables.

 (ctx, id, name) => 
    (ctx.Employee
     .Join(ctx.Contact, e=> e.ContactId, c => c.Id), (emp, cont) => new Container<Employee, Customer> {Employee = emp, Contact = cont})
    .Where(EmployeePredicates.CustomerPredicate(id))
    .Where(EmployeePredicates.NamePredicate(name))
    .Select(t => new EmployeeDTO {Name = t.cont.Name, Customer = e.emp.Customer})

Because each Where() operates on something of type T and returns something of type T, the WherePredicates in the code above must work on the type Container. This makes it very hard to reuse the Predicates. And reuse was the initial goal of this approach...

Sander_V