Busco una manera de combinar dos expresiones lambda, sin utilizar un Expression.Invoke
a cada expresión. En esencia, quiero construir una nueva expresión que encadene dos separadas. Considere el siguiente código:Combinar Expresiones Lambda
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; }
}
Y digamos que tenía dos expresiones:
Expression<Func<Model, Foo>> expression1 = m => m.SubModel.Foo;
Expression<Func<Foo, string>> expression2 = f => f.Bar.Value;
Y quiero unirlas para obtener funcionalmente la siguiente expresión:
Expression<Func<Model, string>> joinedExpression = m => m.SubModel.Foo.Bar.Value;
El único que se me ocurrió hacer esto es utilizar un ExpressionVisitor así:
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);
}
}
Y entonces su utilizarse como esto:
var expExt = new ExpressionExtender<Model, Foo>(expression1);
var joinedExpression = expExt.Extend(expression2);
Funciona, pero se siente un poco torpe para mí. Todavía estoy tratando de envolver mis expresiones de cabeza y me pregunto si hay una manera más idiomática de expresar esto, y tengo la sospecha astuta de que me falta algo obvio.
La razón Quiero hacer esto es para usarlo con los ASP.net MVC 3 ayudantes HTML. Tengo algunas ViewModels profundamente anidados y algunas extensiones HtmlHelper que ayudan a tratar con aquellos, por lo que la expresión tiene que ser sólo una colección de MemberExpressions
para el construido en el MVC ayudantes para procesar correctamente y construir el nombre correctamente anidado valores de atributo. Mi primer instinto fue utilizar Expression.Invoke()
e invocar la primera expresión y encadenarla a la segunda, pero a los ayudantes MVC no les gustó mucho. Perdió su contexto jerárquico.
+1 Esto tiene mucho sentido ahora que lo veo. Una cosa que no mencioné en la pregunta original: ¿hay alguna manera de hacer esto sin mutar ni la expresión inicial? Por ejemplo, tengo una expresión principal que necesito extender de varias maneras diferentes, generando nuevas expresiones con cada invocación. –
@ 32bitkid ¡sí! la expresión es inmutable; ¡No he mutado a ninguno de ellos! –
Muchas gracias por su ayuda nuevamente. –