0

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.

Rookian
  • 19,841
  • 28
  • 110
  • 180
  • This might answer your question. http://stackoverflow.com/questions/627838/asp-net-mvc-getting-querystring-values – Ademar Jun 21 '12 at 15:39
  • @Ademar No it does not. The parameter names / property names are only known at runtime. I try to validate user input data using the RemoteAttribute attribute. I want to have a single action method that handles remote validations for properties that should be unique. – Rookian Jun 21 '12 at 15:44

4 Answers4

1

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 :)

Community
  • 1
  • 1
Rookian
  • 19,841
  • 28
  • 110
  • 180
0

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.

Kevin Aenmey
  • 13,259
  • 5
  • 46
  • 45
  • How does IoC relate to my question :o? – Rookian Jun 21 '12 at 16:29
  • MVC will use your IoC container to resolve model binders. If you create your own model binder, you will need to register it with your IoC container. – Kevin Aenmey Jun 21 '12 at 17:09
  • why do I have to write my own model binder? – Rookian Jun 21 '12 at 17:14
  • If you have any data in your QueryString that cannot be bound with the DefaultModelBinder (e.g. you may want to do some calculation before binding), you will need to create your own model binder to explicitly tell MVC how your values should map to your model. If the DefaultModelBinder can bind your values then you don't need to create your own model binder and, as many people have suggested, you can use the UpdateModel or TryUpdateModel method on the controller. – Kevin Aenmey Jun 21 '12 at 18:04
0

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.

Joel Etherton
  • 37,325
  • 10
  • 89
  • 104
0

You could use TryUpdateModel

public ContentResult TestAction()
{
   var model = new MyModel();

   if(TryUpdateModel(model, new QueryStringValueProvider(ControllerContext)))
   {
      return Content("success");
   }

   return Content("failed");
}
VJAI
  • 32,167
  • 23
  • 102
  • 164
  • @Rookian You asked a question I gave an answer. It's upto you to try or not. – VJAI Jun 21 '12 at 17:28
  • Well if you know the model TryUpdateModel works fine. But I dont know the model, but the type of it. – Rookian Jun 22 '12 at 08:35
  • Are you doing this process in model binder? But knowing the type itself enough I think. You have to check this post http://prideparrot.com/blog/archive/2012/6/gotchas_in_explicit_model_binding – VJAI Jun 22 '12 at 09:04