Converte a expressão Linq “obj => obj.Prop” em “parent => parent.obj.Prop”

Eu tenho uma expressão existente do tipo Expression<Func> ; contém valores como cust => cust.Name .

Eu também tenho uma class pai com um campo do tipo T Eu preciso de um método que aceita o acima como um parâmetro e gera uma nova expressão que leva a class pai ( TModel ) como um parâmetro. Isso será usado como um parâmetro de expressão de um método MVC.

Assim, cust => cust.Name se torna parent => parent.Customer.Name .

Da mesma forma, cust => cust.Address.State se torna parent => parent.Customer.Address.State .

Aqui está minha versão inicial:

  //note: the FieldDefinition object contains the first expression //described above, plus the MemberInfo object for the property/field //in question public Expression<Func> ExpressionFromField(FieldDefinition field) where TModel: BaseModel { var param = Expression.Parameter(typeof(TModel), "t"); //Note in the next line "nameof(SelectedItem)". This is a reference //to the property in TModel that contains the instance from which //to retrieve the value. It is unqualified because this method //resides within TModel. var body = Expression.PropertyOrField(param, nameof(SelectedItem)); var member = Expression.MakeMemberAccess(body, field.Member); return Expression.Lambda<Func>(member, param); } 

O erro que estou recebendo atualmente é quando eu tenho um campo com várias partes (ou seja, cust.Address.State vez de apenas cust.Name ). Eu recebo um erro na linha de var member que o membro especificado não existe – o que é verdade, desde que o corpo em que se refere ao filho do pai ( Customer ) e não o item que contém o membro ( Address ).

Aqui está o que eu gostaria de poder fazer:

  public Expression<Func> ExpressionFromField(FieldDefinition field) where TModel: BaseModel { var param = Expression.Parameter(typeof(TModel), "t"); var body = Expression.PropertyOrField(param, nameof(SelectedItem)); var IWantThis = Expression.ApplyExpressionToField(field.Expression, body); return Expression.Lambda<Func>(IWantThis, param); } 

Qualquer ajuda para chegar a este ponto seria muito apreciada.

Edit: Isto foi marcado como uma possível duplicação desta questão ; no entanto, a única semelhança real é a solução (que é, de fato, a mesma). A composição de expressões não é uma solução intuitiva para acessar propriedades aninhadas por meio de expressões (a menos que a própria inuição seja guiada por determinada experiência, o que não deve ser assumido). Também editei a questão para observar que a solução precisa ser adequada para um parâmetro de um método MVC, o que limita as possíveis soluções.

   

O que você está procurando é a capacidade de compor expressões, assim como você pode compor funções:

 public static Expression> Compose( this Expression> first, Expression> second) { return Expression.Lambda>( second.Body.Replace(second.Parameters[0], first.Body), first.Parameters[0]); } 

Isso depende do método a seguir para replace todas as instâncias de uma expressão por outra:

 public class ReplaceVisitor:ExpressionVisitor { private readonly Expression from, to; public ReplaceVisitor(Expression from, Expression to) { this.from = from; this.to = to; } public override Expression Visit(Expression ex) { if(ex == from) return to; else return base.Visit(ex); } } public static Expression Replace(this Expression ex, Expression from, Expression to) { return new ReplaceVisitor(from, to).Visit(ex); } 

Agora você pode pegar uma expressão selecionando uma propriedade:

 Expression> propertySelector = cust => cust.Name; 

E uma expressão selecionando esse object do modelo:

 Expression> modelSelector = model => model.Customer; 

e componha-os:

 Expression magic = modelSelector.Compose(propertySelector);