I am looking for a way to combine two lambda expressions without using Expression.Invoke in any expression. I essentially want to build a new expression that combines two separate ones. Consider the following code:
class Model { public SubModel SubModel { get; set;} } class SubModel { public Foo Foo { get; set; } } class Foo { public Bar Bar { get; set; } } class Bar { public string Value { get; set; } }
And let's say I had two expressions:
Expression<Func<Model, Foo>> expression1 = m => m.SubModel.Foo; Expression<Func<Foo, string>> expression2 = f => f.Bar.Value;
And I want to combine them together to functionally get the following expression:
Expression<Func<Model, string>> joinedExpression = m => m.SubModel.Foo.Bar.Value;
The only way I could do this is to use ExpressionVisitor as follows:
public class ExpressionExtender<TModel, TIntermediate> : ExpressionVisitor { private readonly Expression<Func<TModel, TIntermediate>> _baseExpression; public ExpressionExtender(Expression<Func<TModel, TIntermediate>> baseExpression) { _baseExpression = baseExpression; } protected override Expression VisitMember(MemberExpression node) { _memberNodes.Push(node.Member.Name); return base.VisitMember(node); } private Stack<string> _memberNodes; public Expression<Func<TModel, T>> Extend<T>(Expression<Func<TIntermediate, T>> extend) { _memberNodes = new Stack<string>(); base.Visit(extend); var propertyExpression = _memberNodes.Aggregate(_baseExpression.Body, Expression.Property); return Expression.Lambda<Func<TModel, T>>(propertyExpression, _baseExpression.Parameters); } }
And then it can be used as follows:
var expExt = new ExpressionExtender<Model, Foo>(expression1); var joinedExpression = expExt.Extend(expression2);
It works, but for me it is a bit awkward. I am still trying to wrap my expressions and wonder if there is a more idiomatic way of expressing this, and I have a hidden suspicion that I will miss something obvious.
The reason I want to do this is to use it with ASP.NET mvc 3 Html helpers. I have some deeply nested ViewModels and some HtmlHelper extensions that help deal with them, so the expression should just be a MemberExpressions set for MVC built-in helpers to handle them correctly and create the correct deeply nested name attribute values. My first instinct was to use Expression.Invoke() and call the first expression and associate it with the second, but MVC helpers didn't really like that. He lost his hierarchical context.