Merge two expressions of same type (without bool)

116 views Asked by At

I have a method which takes expression Expression<Func<TFoo, string>> exp.

I can pass single expression like this

MyMethod(o => o.SomeStringProperty);

But now i want to combine expressions (from two string properties) and pass in this method

Every other example I found is of Expression<Func<Foo, bool>>.

I tried

Expression<Func<TFoo, string>> fn1 = x => x.SomeStringProperty1;
Expression<Func<TFoo, string>> fn2 = x => x.SomeStringProperty2;

var body = Expression.Coalesce(fn1.Body, fn2.Body);
var lambda = Expression.Lambda<Func<TFoo, string>>(body, fn1.Parameters[0]);

but almost every function of Expression throws exception. How to do this combine?

1

There are 1 answers

0
Ivan Stoev On BEST ANSWER

When combining lambda expressions, you should make sure they are bound to the one and the same parameter instances used in the resulting expression.

It can be achieved in two ways.

The first is to use Expression.Invoke method:

var body = Expression.Coalesce(fn1.Body, Expression.Invoke(fn2, fn1.Parameters[0]));
var lambda = Expression.Lambda<Func<TFoo, string>>(body, fn1.Parameters[0]);

This is the simplest method, but creates unsupported expression for Entity Framework and similar which do not support invocation expressions.

The second approach uses a simple parameter replacer helper to rebind the second lambda expression body to the first lambda expression parameter:

public static class ExpressionUtils
{
    public static Expression ReplaceParameter(this Expression expression, ParameterExpression source, Expression target)
    {
        return new ParameterReplacer { Source = source, Target = target }.Visit(expression);
    }
    class ParameterReplacer : ExpressionVisitor
    {
        public ParameterExpression Source;
        public Expression Target;
        protected override Expression VisitParameter(ParameterExpression node)
        {
            return node == Source ? Target : base.VisitParameter(node);
        }
    }
}

like this

var body = Expression.Coalesce(fn1.Body, 
    fn2.Body.ReplaceParameter(fn2.Parameters[0], fn1.Parameters[0]));
var lambda = Expression.Lambda<Func<TFoo, string>>(body, fn1.Parameters[0]);