I have an action method without parameters.
The QueryString
collection contain all of my values. The keys of the QueryString
match my view model properties.
var queryStringValueProvider = new QueryStringValueProvider(ControllerContext);
var providerResult = queryStringValueProvider.GetValue(ValidationKeys.Id); // ?!
var viewModelTypeName = queryString[ValidationKeys.ViewModelType];
var viewModelType = Type.GetType(viewModelTypeName);
var viewModelInstance = providerResult.ConvertTo(viewModelType); // throws an InvalidOperationException
How can I convert the QueryString
collection to a view model?
ASP.NET MVC already do this when you just pass the view model into the action method parameters. So what I need is an afterwards model binding using ASP.NET MVC mechanics.
What you're asking for is serialization. For doing this simply, you could put a constructor overload that accepts a QueryStringValueProvider as an argument and that constructor is responsible initializing all of the model's properties based on the provider. If you stick to strings, you could very easily put such a constructor into a model base class that could be inherited by all of your models.
This could also be built into an extension method so it could be called "on demand" rather than at construction.
To manually do custom model binding, create a custom model binder (implement IModelBinder
) and register it with your IoC container.
Or you could call this.UpdateModel
on inside your action method. This should bind the values from your ValueProvider (RouteData, Request.Form collection and QueryString) to your model.
You could use TryUpdateModel
public ContentResult TestAction()
{
var model = new MyModel();
if(TryUpdateModel(model, new QueryStringValueProvider(ControllerContext)))
{
return Content("success");
}
return Content("failed");
}
My Controller Action
var viewModelTypeName = queryString[ValidationKeys.ViewModelType];
var viewModelType = Type.GetType(viewModelTypeName);
var instance = Activator.CreateInstance(viewModelType);
UpdateModelUsingQueryString(instance);
UpdateModel
protected internal void UpdateModelUsingQueryString<TModel>(TModel model) where TModel : class
{
if (model == null) throw new ArgumentNullException("model");
Predicate<string> propertyFilter = propertyName => new BindAttribute().IsPropertyAllowed(propertyName);
var binder = Binders.GetBinder(typeof(TModel));
var bindingContext = new ModelBindingContext()
{
ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => model, model.GetType()),
ModelState = ModelState,
PropertyFilter = propertyFilter,
ValueProvider = new QueryStringValueProvider(ControllerContext)
};
binder.BindModel(ControllerContext, bindingContext);
}
The problem was that UpdateModel
or TryUpdateModel
does not work for object
by design.
Both methods use typeof(TModel)
. But you have to use model.GetType()
.
Take a look at: Model Binding - Type in External Assembly
Darin Dimitrov gave the right answer :)