Iterieren über Eigenschaften eines Lambda-Ausdrucks

.net c# expression-trees lambda

Frage

Ich versuche, ein komplexes Steuerelement, das in meiner Website häufig, aber mit verschiedenen Feldern verwendet wird, zu generalisieren. Die Funktionalität in der Steuerung ist immer gleich, nur die zugrunde liegenden Felder ändern sich.

Um die Methode zum Anzeigen verschiedener Felder zu erreichen, versuche ich eine HTMLHelper-Erweiterung zu erstellen, die einen Expression<Func<TModel,TProperty>> als Parameter akzeptiert, der die Eigenschaften einer Klasse enthalten würde, die für die Anzeige im Steuerelement benötigt wird. Beispielsweise:

Die Aussicht:

@model Project.Core.Page
@Html.MyHelper(p => new { p.Author.Name, p.Author.Location, p.Author.Age });

Es ist die Erweiterung, mit der ich Probleme habe - wie kann ich über die bereitgestellten Parameter im Lambda iterieren, um jeweils ein TextBoxFor() , oder manuell ein input erstellen und es mit dem value und dem name des Lambda-Parameters TextBoxFor() ?

Die Erweiterung in psuedo:

public static MvcHtmlString MyHelper<TModel,TProperty>(
    this HtmlHelper<TModel> helper, 
    Expression<Func<TModel,TProperty>> expression) {
    foreach (var parameter in expression.???) {
        // helper.TextBoxFor(???) 
        // TagBuilder("input").Attributes("name", expression.???)
    }
}

Ich habe das Gefühl, dass ich das viel zu lange angeguckt habe, und ich habe auch das Gefühl, dass ich es auf eine einfachere Weise übersehe, dies zu erreichen.

Jede Hilfe wird sehr geschätzt. Wenn Sie weitere Informationen benötigen oder etwas Wichtiges verpasst haben, lassen Sie es mich wissen.

Akzeptierte Antwort

Wenn Sie Folgendes annehmen:

  1. Das Ergebnis des Eingabeausdrucks ist eine Projektion (gibt ein neues Objekt zurück, anonym oder nicht)
  2. Die Elemente der Projektion sind alle MemberExpressions und enthalten keinen Aufruf einer Methode für das Modell oder seine MemberExpressions Elemente

Dann können Sie erreichen, was Sie wollen, indem Sie den folgenden Ansatz verwenden:

Bearbeiten:

Nachdem ich festgestellt habe, dass mein erstes Beispiel keine Objekte mit komplexen Eigenschaften verarbeiten konnte, habe ich den Code aktualisiert, um eine Hilfsmethode für den Zugriff auf Eigenschaftswerte zu verwenden. Diese Methode verwendet die Rekursion, um die entsprechenden Werte zurückzugeben.

public static MvcHtmlString MyHelper<TModel,object>(
    this HtmlHelper<TModel> helper, 
    Expression<Func<TModel,object>> expression) {

        var newExpression = expression.Body as NewExpression;
        TModel model = helper.ViewData.Model;

        foreach (MemberExpression a in newExpression.Arguments) {

            var propertyName = a.Member.Name;
            var propertyValue = GetPropertyValue<TModel>(model, a);

            // Do whatever you need to with the property name and value;

        }

    }

    private static object GetPropertyValue<T>(T instance, MemberExpression me) {

        object target;

        if (me.Expression.NodeType == ExpressionType.Parameter) {
            // If the current MemberExpression is at the root object, set that as the target.            
            target = instance;
        }
        else {                
            target = GetPropertyValue<T>(instance, me.Expression as MemberExpression);
        }

        // Return the value from current MemberExpression against the current target
        return target.GetType().GetProperty(me.Member.Name).GetValue(target, null);

    }

Hinweis: Ich habe dies nicht direkt als MVC-Erweiterungsmethode in meiner IDE implementiert, daher ist möglicherweise eine geringfügige Änderung der Syntax erforderlich.


Beliebte Antwort

Der von Ihnen erstellte Ausdruck ist relativ kompliziert - Sie müssen alle Eigenschaften abrufen und dann den anonymen Typkonstruktor aufrufen. "Disassembling", das kann schmerzhaft werden ... obwohl, wenn Sie immer noch versuchen möchten, würde ich vorschlagen, eine leere Methodenimplementierung zu verlassen und im Debugger nachzusehen, wie der Ausdruck aussieht.

Wenn Sie sich mit einer etwas hässlicheren Form des Aufrufcodes begnügen würden, wäre es viel einfacher, dies zu implementieren:

@Html.MyHelper(p => p.Author.Name, p => p.Author.Location, p => p.Author.Age);

Sie könnten dafür sorgen, dass ein params Expression<TModel, object> oder Sie könnten mehrere Überladungen mit einer anderen Anzahl von Parametern deklarieren, z

// Overload for one property
MyHelper<TModel, TProperty1>(this ..., Expression<Func<TModel, TProperty1>> func1)

// Overload for two properties
MyHelper<TModel, TProperty1, TProperty2>(this ...,
   Expression<Func<TModel, TProperty1>> func1,
   Expression<Func<TModel, TProperty2>> func2)

etc.



Lizenziert unter: CC-BY-SA with attribution
Nicht verbunden mit Stack Overflow
Ist diese KB legal? Ja, lerne warum
Lizenziert unter: CC-BY-SA with attribution
Nicht verbunden mit Stack Overflow
Ist diese KB legal? Ja, lerne warum