Konvertieren Sie den Linq-Ausdruck “obj => obj.Prop” in “parent => parent.obj.Prop”

Ich habe einen vorhandenen Ausdruck vom Typ Expression<Func> ; Es enthält Werte wie cust => cust.Name .

Ich habe auch eine Elternklasse mit einem Feld vom Typ T Ich brauche eine Methode, die das obige als Parameter akzeptiert und einen neuen Ausdruck generiert, der die TModel ( TModel ) als Parameter verwendet. Dies wird als Ausdrucksparameter einer MVC-Methode verwendet.

Daher wird cust => cust.Name zu parent => parent.Customer.Name .

Ebenso wird cust => cust.Address.State zu parent => parent.Customer.Address.State .

Hier ist meine erste Version:

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

Der Fehler, den ich gerade cust.Name , ist, wenn ich ein Feld mit mehreren Teilen habe (dh cust.Address.State statt nur cust.Name ). Ich bekomme einen Fehler in der var member Element-Zeile, dass das angegebene Mitglied nicht existiert – das stimmt, da sich der Body auf das Child des Customer bezieht ( Customer ) und nicht auf das Element, das das Mitglied ( Address ) enthält.

Folgendes möchte ich tun:

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

Jede Hilfe zu diesem Punkt würde sehr geschätzt werden.

Edit: Dies wurde als ein mögliches Duplikat dieser Frage markiert; Die einzige wirkliche Ähnlichkeit ist jedoch die Lösung (die tatsächlich die gleiche ist). Das Zusammensetzen von Ausdrücken ist keine intuitive Lösung für den Zugriff auf verschachtelte Eigenschaften über Ausdrücke (es sei denn, die Inuition wird von bestimmten Erfahrungen geleitet, die nicht angenommen werden sollten). Ich habe auch die Frage bearbeitet, um zu bemerken, dass die Lösung für einen Parameter einer MVC-Methode geeignet sein muss, was die möglichen Lösungen begrenzt.

Was Sie suchen, ist die Fähigkeit, Ausdrücke zu verfassen, genau wie Sie functionen zusammenstellen können:

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

Dies beruht auf der folgenden Methode, um alle Instanzen eines Ausdrucks durch einen anderen zu ersetzen:

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

Sie können nun einen Ausdruck auswählen, der eine Eigenschaft auswählt:

 Expression> propertySelector = cust => cust.Name; 

Und ein Ausdruck, der dieses Objekt aus dem Modell auswählt:

 Expression> modelSelector = model => model.Customer; 

und komponiere sie:

 Expression magic = modelSelector.Compose(propertySelector);