How to reuse lambda parameter across multiple combined expressions

If you’re having multiple separately defined expressions having common lambda parameter like these:

Expression<Func> pre1 = u => u.FirstName != null;
Expression<Func> pre2 = u => u.MiddleName != null;
Expression<Func> pre3 = u => u.LastName != null;

And trying to combine them into one and to reuse the said parameter like this:

ParameterExpression param = Expression.Parameter(typeof(User), "u");

var predicates = GetPredicates();
var body = predicates.Select(exp => exp.Body)
                     .Aggregate((left, right) => Expression.AndAlso(left, right));
Expression<Func> lambda = Expression.Lambda<Func>(body, param);
Func func = lambda.Compile();

Then you’ll likely get an exception:

Unhandled Exception: System.InvalidOperationException: variable ‘u’ of type ‘User’ referenced from scope ”, but it is not defined.

The reason is that spite the lambda parameters have same type and name they’re defined in different expressions thus in different scopes so can’t be simply reused.

What you need to do is to use single parameter across all expressions, in other words to unify/merge/replace their parameters with one:

class ParameterReplacer : ExpressionVisitor
{
  private readonly ParameterExpression _param;

  private ParameterReplacer(ParameterExpression param)
  {
    _param = param;
  }

  protected override Expression VisitParameter(ParameterExpression node)
  {
    return node.Type == _param.Type ? // if types match on both of ends
      base.VisitParameter(_param) : // replace
      node; // ignore
  }

  public static T Replace(ParameterExpression param, T exp) where T : Expression
  {
    return (T)new ParameterReplacer(param).Visit(exp);
  }
}

And then:

var body = predicates.Select(exp => exp.Body)
                     .Select(exp => ParameterReplacer.Replace(param, exp))
                     .Aggregate((left, right) => Expression.AndAlso(left, right));

var lambda = Expression.Lambda<Func>(body, param);

That’s it, now compilation will work fine and produce the desired predicate.

Happy expressions building!

This entry was posted in Programming and tagged , . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

w

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.