I am working on an MVC project and would like to pass the Html.TextboxFor method the name of a property. This is my viewmodel
public class RuleViewModel<T> where T : class, IValidatableObject
{
private T _rule;
public T RuleModel
{
get
{
return _rule
?? (_rule = Activator.CreateInstance<T>());
}
}
public RuleMetadata Metadata { get; set; }
public Expression<Func<RuleViewModel<T>, Object>> GetParameterByName(PropertyInfo pi)
{
var fieldName = Expression.Parameter(typeof(RuleViewModel<T>), "x");
var fieldExpression = Expression.PropertyOrField(Expression.Property(fieldName, "RuleModel"), pi.Name);
var exp = Expression.Lambda<Func<RuleViewModel<T>, Object>>(fieldExpression, fieldName);
return exp;
}
}
Then in the view I do this
@foreach (var prop in Model.RuleModel.GetType().GetProperties())
{
var result = Model.Metadata.Columns.SingleOrDefault(m => m.ColumnName == prop.Name);
if (result != null)
{
<td>
@Html.TextBoxFor(Model.GetParameterByName(prop))
</td>
}
}
The problem is that when the property is of type decimal?, I get a cannot convert nullable decimal to object error. I looked around and found that you can use Expression.Convert to fix this but when I do that I get an error on the view
Templates can be used only with field access, property access, single-dimension array index, or single-parameter custom indexer expressions.
Any help would be appreciated. This is a proof of concept project that I'm working on and without this piece it is dead in the water.
The problem is that you can't just use object
as TProperty
when calling TextBoxFor<TModel, TProperty>()
. It expects a lambda expression of the form Func<TModel, TProperty>
, and the variance rules of C# are such that a Func<TModel, decimal?>
is not assignment compatible with Func<TModel, object>
. You also can't simply use Convert()
, because the MVC internals won't accept a lambda whose body is a Convert
expression.
What you can do is use dynamic binding to invoke TextBoxFor<TModel, TProperty>()
with the correct type arguments:
public Expression GetParameterByName(PropertyInfo pi)
{
var fieldName = Expression.Parameter(typeof(RuleViewModel<T>), "x");
var fieldExpression = Expression.PropertyOrField(
Expression.Property(fieldName, "RuleModel"),
pi.Name);
var exp = Expression.Lambda(
typeof(Func<,>).MakeGenericType(typeof(RuleViewModel<T>), fieldExpression.Type),
fieldExpression,
fieldName);
return exp;
}
// ...
@InputExtensions.TextBoxFor(Html, (dynamic)Model.GetParameterByName(prop))
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With