Sto cercando un modo per combinare due espressioni lambda, senza utilizzare un Expression.Invoke
su nessuna espressione. Voglio essenzialmente costruire una nuova espressione che ne incatena due separati. Si consideri il seguente codice:Combine Lambda Expressions
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; }
}
e permette di dire che ho avuto due espressioni:
Expression<Func<Model, Foo>> expression1 = m => m.SubModel.Foo;
Expression<Func<Foo, string>> expression2 = f => f.Bar.Value;
E voglio unirmi a loro insieme per ottenere funzionalmente la seguente espressione:
Expression<Func<Model, string>> joinedExpression = m => m.SubModel.Foo.Bar.Value;
L'unica modo che potrei pensare di fare questo è quello di utilizzare un ExpressionVisitor come questo:
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);
}
}
E poi la sua utilizzati in questo modo:
var expExt = new ExpressionExtender<Model, Foo>(expression1);
var joinedExpression = expExt.Extend(expression2);
Funziona, ma ci si sente un po 'goffo per me. Sto ancora cercando di avvolgere le mie espressioni di testa e mi chiedo se c'è un modo più idiomatico per esprimere questo, e ho il minimo sospetto che mi manca qualcosa di ovvio.
Il motivo io voglio fare questo è quello di utilizzare con le aiutanti ASP.net MVC 3 Html. Ho alcuni ViewModels profondamente nidificati e alcune estensioni HtmlHelper che aiutano a gestirli, quindi l'espressione deve essere solo una raccolta di MemberExpressions
per gli helper MVC incorporati per elaborarli correttamente e creare i valori degli attributi del nome correttamente nidificati. Il mio primo istinto era usare Expression.Invoke()
e invocare la prima espressione e incatenarla al secondo, ma gli assistenti MVC non gli piacevano molto. Ha perso il suo contesto gerarchico.
+1 Questo ha molto senso ora che lo vedo. Una cosa che ho omesso di menzionare nella domanda iniziale: esiste un modo per farlo senza mutare l'espressione di partenza. Ad esempio, ho un'espressione di base che ho bisogno di estendere in molti modi diversi, generando nuove espressioni con ogni invocazione. –
@ 32bitkid si! l'espressione è immutabile; Non ho mutato nessuno dei due! –
Grazie mille per il tuo aiuto. –