Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Build IQueryable.Any with Expression Trees for LINQ queries

I'm building a SQL "WHERE" clause dynamically using the System.Linq.Expressions.Expression class. It works well for simple clauses, e.g. to add "PhaseCode = X" clause, I do the following:

var equalTarget = Expression.Constant(phaseCode, typeof(int?));
var phaseEquals = Expression.Equal(Expression.PropertyOrField(projParam, "PhaseCode"), equalTarget);

However, now I'm trying to build an expression that will returns the record if a project has been assigned to a particular group. Project and Group has many-to-many relationship. Without the expression trees, I would do it as follows:

db.Projects.Where(p => .... && p.GroupsAssigned.Any(g => g.ID == groupId))

However, I can't seem to find a way to express that with the Expression class. There are actually two things I can't figure out:

  • How to traverse the relationships between tables
  • How to do x.Any()

Any help is greatly appreciated.

like image 770
Alec Bryte Avatar asked Mar 12 '13 17:03

Alec Bryte


1 Answers

Calling an extension method, like Enumerable.Any or Queryable.Any, is simply a static method call on the sequence and the lambda expression you created for the WHERE clause. You can use Expression.Call to do this:

// for Enumerable.Any<T>(IEnumerable<T>,Predicate<T>)
var overload = typeof(Enumerable).GetMethods("Any")
                                 .Single(mi => mi.GetParameters().Count() == 2);
var call = Expression.Call(
    overload,
    Expression.PropertyOrField(projParam, "GroupsAssigned"),
    anyLambda);      

For Queryable.Any<T>, you'll need to roll this up into a method:

static Expression BuildAny<TSource>(Expression<Func<TSource, bool>> predicate)
{
    var overload = typeof(Queryable).GetMethods("Any")
                              .Single(mi => mi.GetParameters().Count() == 2);
    var call = Expression.Call(
        overload,
        Expression.PropertyOrField(projParam, "GroupsAssigned"),
        predicate);   

    return call;
}

Although this seems odd that you're unable to do this through a normal query.

like image 72
user7116 Avatar answered Oct 05 '22 05:10

user7116