Expression lambdas to access sub properties
I'm building a MVC HTML helper that exposes multiple properties of a class.
This is my class:
public class Foo {
public string Section { get; set; }
public string Value { get; set; }
}
And here is my helper:
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 );
}
}
Then, inside my view, I call the helper
@(Html.Foo().DropDownFooListFor( x => x.Bar ))
And here is my view model:
public class Baz {
public Foo Bar { get; set; }
}
My problem is that if I uncomment the three lines commented out (i.e: use the list
expression), it miserably fails.
I don't get why using value
works as expected but using list
doesn't.
I get the following exception:
variable 'x' of type 'Namespace.Baz' referenced from scope '', but it is not defined
Again, Baz
is my view model.
What am I doing wrong?
Edit: Ok, this is worse that I thought. It works if I use any of my expr开发者_C百科essions with DropDownListFor
, but it doesn't with HiddenFor
, or TextBoxFor
.
Edit 2: Here is how Helper
is defined.
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 };
}
}
Create the two lambdas using the parameter which is in scope from the main expression (i.e. 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]
);
Now you can uncomment the HiddenFor
call and it's gonna work.
精彩评论