I have a static generic FormBuilder HTML helper method (extension method on the HTMLHelper class) that takes a generic argument of the view model type and then, when passed one or more string property names from the database, generates a HTML form in ASP.NET MVC 5.1 with .NET 4.5.
I have a single public method to generate the form, and separate private methods to generate the "module" sections within the form and then render each field within those. Type arguments are passed down this chain from top to bottom.
In the "RenderField" method I create a typed HtmlHelper using the code-
var typedHelper = helper as HtmlHelper<TModel>;
Where helper is the HtmlHelper extended in the RenderForm method.
I then create an expression using the code-
var modelType = typeof(TModel);
...
var modelProperty = modelType.GetProperty(field.PropertyName);
if (modelProperty == null)
{
Elmah.ErrorSignal.FromCurrentContext().Raise(new ArgumentException(string.Format("Model {0} does not contain property {1}", modelType.Name, field.PropertyName)));
return null;
}
var modelPropertyType = modelProperty.PropertyType;
var parameter = Expression.Parameter(modelType, "m");
var property = Expression.Property(parameter, field.PropertyName);
var expression = Expression.Lambda<Func<TModel, object>>(property, parameter);
This I can then later use to create an EditorFor, DisplayFor, or ValidationMessageFor as follows-
fieldContainer.InnerHtml += typedHelper.EditorFor(expression);
fieldContainer.InnerHtml += typedHelper.ValidationMessageFor(expression);
This works for string editors, but if I try a nullable datetime then I get the error-
Expression of type 'System.Nullable`1[System.DateTime]' cannot be used for return type 'System.Object'
If I try and convert the property to an object as I've seen in a Jon Skeet answer by changing the following line-
var expression = Expression.Lambda<Func<TModel, object>>(Expression.Convert(property, typeof(object)), parameter);
And change the editors code to -
var compiledExpression = expression.Compile()(model);
fieldContainer.InnerHtml += typedHelper.EditorFor(compiledExpression);
fieldContainer.InnerHtml += typedHelper.ValidationMessageFor(compiledExpression);
I get the error message that the type arguments cannot be inferred from usage.
If I change the return type to "dynamic" then it states "Extension Methods cannot be dynamically dispatched".
I cannot specify "modelPropertyType" as the return argument for the generic method - presumably as it is not guaranteed to be a concrete type at compile time.
Is there any way I can dynamically specify the return type of the expression to be that of the property at run time so I can use the EditorFor helper methods provided by ASP.NET MVC?