Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Create dynamic Expression lambda from two others (chaining the Expressions)

Given a lambda that takes an Identification object, and returns a property:

Expression<Func<Identification, object>> fx = _ => _.Id;

And a conversion lambda that converts an object into an Identification instance:

ParameterExpression p = Expression.Parameter(typeof(object), "o");
Expression @new = Expression.Lambda(Expression.Convert(p, typeof(Identification)), p);

How do I build a new lambda that executes @new (getting out the Identification Instance) and passes the result into fx. I need @new's result to bind to the first parameter of fx somehow, and I cannot find an example.

I need the result to be an Expression, it should be of type Expression<Func<object, object>> and it should convert the inbound parameter to an Identification and then get the Id property.

like image 434
Jim Avatar asked Oct 24 '11 09:10

Jim


2 Answers

Firstly, note that this is easier if you type @new appropriately, i.e.:

LambdaExpression @new = ...

since that provides easy access to @new.Body and @new.Parameters; that done, Expression.Invoke can be useful here:

var combinedParam = Expression.Parameter(typeof(object), "o");
var combined = Expression.Lambda<Func<object, object>>(
    Expression.Invoke(fx,
        Expression.Invoke(@new, combinedParam)), combinedParam);

although for a cleaner expression, you can also use ExpressionVisitor to completely replace the inner expressions:

var injected = new SwapVisitor(fx.Parameters[0], @new.Body).Visit(fx.Body);
var combined = Expression.Lambda<Func<object, object>>(injected,@new.Parameters);

with:

class SwapVisitor : ExpressionVisitor {
    private readonly Expression from, to;
    public SwapVisitor(Expression from, Expression to) {
        this.from = from;
        this.to = to;
    }
    public override Expression Visit(Expression node) {
        return node == from ? to : base.Visit(node);
    }
}

what this does is:

  • inspect the fx.Body tree, replacing all instances of _ (the parameter) with the @new.Body (note that this will contain references to the o parameter (aka p)
  • we then build a new lambda from the replaced expression, re-using the same parameters from @new, which ensures that the values we injected will be bound correctly
like image 170
Marc Gravell Avatar answered Nov 14 '22 18:11

Marc Gravell


Using the code from Marc Gravell's answer, you can simplify this really nicely with a helper function:

public static class ExpressionHelper {
    public static Expression<Func<TFrom, TTo>> Chain<TFrom, TMiddle, TTo>(
        this Expression<Func<TFrom, TMiddle>> first,
        Expression<Func<TMiddle, TTo>> second
    ) {
        return Expression.Lambda<Func<TFrom, TTo>>(
           new SwapVisitor(second.Parameters[0], first.Body).Visit(second.Body),
           first.Parameters
        );
    }

    private class SwapVisitor : ExpressionVisitor {
        private readonly Expression _from;
        private readonly Expression _to;

        public SwapVisitor(Expression from, Expression to) {
            _from = from;
            _to = to;
        }

        public override Expression Visit(Expression node) {
            return node == _from ? _to : base.Visit(node);
        }
    }
}

Now look how clean that is!

var valueSelector = new Expression<Func<MyTable, int>>(o => o.Value);
var intSelector = new Expression<Func<int, bool>>(x => x > 5);
var selector = valueSelector.Chain<MyTable, int, bool>(intSelector);

And it works with Entity Framework and other things that need a clean Expression that doesn't try to invoke a Func within it.

like image 3
ErikE Avatar answered Nov 14 '22 18:11

ErikE