2011-07-22 5 views
1

Je construis une aide HTML MVC qui expose plusieurs propriétés d'une classe.Expression lambdas pour accéder aux sous-propriétés

C'est ma classe:

public class Foo { 
    public string Section { get; set; } 
    public string Value { get; set; } 
} 

Et voici mon aide:

public partial class FooBuilder<TModel> { 
    public MvcHtmlString DropDownFooListFor(Expression<Func<TModel, Foo>> expression, string optionLabel = null, IDictionary<string, object> htmlAttributes = null) { 
     var metadata = ModelMetadata.FromLambdaExpression(expression, Helper.ViewData); 
     var model = metadata.Model as Foo; 

     var items = FooUtility.GetFooValues(metadata.PropertyName).Select(x => new SelectListItem { 
      Text = x, 
      Value = x, 
      Selected = model != null && model.Value == x 
     }); 

     var value = Expression.Lambda<Func<TModel, string>>(Expression.MakeMemberAccess(expression.Body, typeof(Foo).GetProperty("Value")), Expression.Parameter(typeof(TModel), "value")); 
     var list = Expression.Lambda<Func<TModel, string>>(Expression.MakeMemberAccess(expression.Body, typeof(Foo).GetProperty("Section")), Expression.Parameter(typeof(TModel), "section")); 

     //Helper.ViewContext.Writer.Write(
     // Helper.HiddenFor(list, new { value = string.Format("{0}#{1}", FooUtility.GetCurrentSection(), metadata.PropertyName) }) 
     //); 

     return Helper.DropDownListFor(value, items, optionLabel, htmlAttributes); 
    } 
} 

Puis, à l'intérieur de mon point de vue, j'appelle l'aide

@(Html.Foo().DropDownFooListFor(x => x.Bar)) 

Et voici ma Afficher le modèle:

public class Baz { 
    public Foo Bar { get; set; } 
} 

Mon problème est que si je décommenter les trois lignes commentées (i.e.: utiliser l'expression list), il échoue lamentablement. Je ne comprends pas pourquoi utiliser value fonctionne comme prévu, mais pas list.

je reçois l'exception suivante:

variable 'x' de type 'Namespace.Baz' référencé de la portée '', mais il ne définit pas

Encore une fois, Baz est mon voir le modèle.

Qu'est-ce que je fais mal?


Modifier: Ok, c'est pire que je pensais. Cela fonctionne si j'utilise l'une de mes expressions avec DropDownListFor, mais pas avec HiddenFor ou TextBoxFor.


Edit 2: Voici comment Helper est défini.

public partial class FooBuilder<TModel> { 
    public HtmlHelper<TModel> Helper { get; set; } 
} 

public static class FooHelpers { 
    public static FooBuilder<TModel> Foo<TModel>(this HtmlHelper<TModel> helper) { 
     return new FooBuilder<TModel> { Helper = helper }; 
    } 
} 
+0

Copie possible: http://stackoverflow.com/questions/6106283/memberexpression-invalidoperationexpression-variable-x-referenced-from-scope –

+0

@NickLarsen Où puis-je créer différents paramètres portant le même nom? –

+0

Par curiosité, le code commenté écrira un caché directement à la sortie, pourquoi faites-vous cela? Aussi, pouvez-vous mettre à jour ce code pour montrer où 'Helper' est défini? –

Répondre

3

Créer deux lambdas en utilisant le paramètre qui est portée de la principale expression (à savoir expression.Parameters[0]):

var value = Expression.Lambda<Func<TModel, string>>(
    Expression.MakeMemberAccess(
     expression.Body, 
     typeof(Foo).GetProperty("Value") 
    ), 
    expression.Parameters[0] 
); 

var list = Expression.Lambda<Func<TModel, string>>(
    Expression.MakeMemberAccess(
     expression.Body, 
     typeof(Foo).GetProperty("Section") 
    ), 
    expression.Parameters[0] 
); 

Maintenant, vous pouvez décommenter l'appel HiddenFor et ça va marcher.

+0

Oui, cela a beaucoup de sens. Merci Darin.Maintenant, je suis confus, avez-vous une idée de la raison pour laquelle mes expressions fonctionnent avec 'Html.DropDownListFor'? –

+0

Juste pour m'assurer que je comprends bien cela, les paramètres de liste et de valeur créaient des expressions différentes pour obtenir la même valeur, mais ils n'avaient pas le contexte de l'expression originale? –

+0

@Bertrand Marron, la raison pour laquelle votre code fonctionne avec 'DropDownListFor' est que vous ne l'appelez jamais à l'intérieur de cet assistant. Vous renvoyez simplement un résultat 'MvcHtmlString' de votre assistant et c'est seulement dans la vue que' .Compile() 'est invoqué sur le lambda par la méthode' @ 'razor. En ce qui concerne 'HiddenFor', si vous regardez attentivement la méthode' Write' que vous utilisez sur 'ViewContext' n'a pas de surcharge qui prend un' MvcHtmlString'. Il en a un qui prend 'object'. C'est ce qui est utilisé et '.ToString()' est appelé immédiatement ... pour continuer –

Questions connexes