1

I have two expressions:

  public static Expression<Func<TSource, TReturn>> Merge<TSource, TSource2, TReturn>(
      Expression<Func<TSource, TSource2>> foo1
      Expression<Func<TSource2, TReturn>> foo2)
  {
      // What to do?
  }

How can I merge them into a single expression, so the output from the first is used as the input for the second? I am new to these and so far its just been exceptions.

Thanks

2
  • can you add a concrete example that shows what you mean? is this as in x => x.Name and s => s.Length to get x => x.Name.Length ? Commented Mar 26, 2013 at 11:41
  • Yeah sorry that's what I meant, for future reference when people come to see this. Commented Mar 26, 2013 at 12:09

1 Answer 1

1

This depends a lot on which providers need to use it. Some will be fine with:

public static Expression<Func<TSource, TReturn>>
     Merge<TSource, TSource2, TReturn>(
  Expression<Func<TSource, TSource2>> foo1,
  Expression<Func<TSource2, TReturn>> foo2)
{
    return Expression.Lambda<Func<TSource, TReturn>>(
      Expression.Invoke(foo2, foo1.Body),
      foo1.Parameters);
}

However, others (EF) will not. You can also re-write the expression-tree with a visitor to inline the expression:

public static Expression<Func<TSource, TReturn>>
      Merge<TSource, TSource2, TReturn>(
  Expression<Func<TSource, TSource2>> foo1,
  Expression<Func<TSource2, TReturn>> foo2)
{
    var swapped = new SwapVisitor(
        foo2.Parameters.Single(), foo1.Body).Visit(foo2.Body);
    return Expression.Lambda<Func<TSource, TReturn>>(
        swapped, foo1.Parameters);
}

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);
    }
}

this will work with all providers.

Sign up to request clarification or add additional context in comments.

3 Comments

This indeed worked! Thanks I was extremely close, but I was using foo1, not foo1.Body in my overload of Invoke.
@Tim see the edit; the inlined version will usually be preferable, since it will work in more places (and can be more direct)
Also thanks for the extended answer. I am using LINQ-to-Entities so likely will be useful in the future. Edit: Ok thanks!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.