views:

367

answers:

2

As I know ValueProviderDictionary takes values for binding from 3 places

  1. From Post form
  2. From Route values
  3. From Query string

I want to be able to disable the second and the third options. The only solution I see here is to create my own custom ValueProvider (copy-paste ValueProviderDictionary source file and remove these two options from there).

Is this solution correct? Are there any more elegant alternatives?

(If the solution with a custom ValueProvider is correct where it should be initialized? In a Controller's action method or in a custom ModelBinder?)

Thank you

+3  A: 

Override Controller.Initialize() and set the ValueProvider property in that method (after calling base.Initialize()).

To avoid reimplementing the entire ValueProviderDictionary, you could just subclass it. The only interesting part is that you'd have to copy a small snippet of ValueProviderDictionary.PopulateDictionary(). In your constructor, call the base constructor, then immediately this.Clear(), followed by this.YourCustomPopulateDictionary(). This should make your code much smaller.

Levi
If I am initializing ValueProvider in controller's Initialize method, as you said, then I won't get any ActionMethod parameter that came not from Form, I will get ArgumentException instead. So the place for initializing ValueProvider should be OnActionExecuting. But now I will still get values from RouteData if Controller will pass populated model as action parameter. So I still don't see 100% solution for this _simple_ problem
mipi
Ah, when you phrase it like that it changes the question. :)Parameter binding is actually nothing more than a glorified call to UpdateModel(), so they're actually the same code path. There are overloads of UpdateModel() that take an IDictionary<string, ValueProviderResult> as a parameter, and you can pass your custom provider there.A better question, though, would be why are you trying to bind from different value providers for model parameters than for calls to UpdateModel()? What is it about your app that differentiates these? This might lead us in a more productive direction.
Levi
I want to bind from different ValueProvider, in my case simple Form values provider, because if I leave the default it can lead to unexpected behaviour. If I leave the default I should _always_ watch out of casual match with RouteData values. For example if Id of route data is not provided it defaults to empty string, and when I UpdateModel when creating the Model, I do not want that Id value at all. And it could happen not only to Id value, but for any other. And I don't want to warry about that every time I add new properties or add new values to the RouteData.
mipi
When I am doing UpdateModel in Post method I want to get values _only_ from posted data. Hanselman in his blog http://bit.ly/uFd0T made a workaround for this problem. He changed the Dinner.Id to Dinner.DinnerID (last comment points it out)!!! This looks completely wrong for me. It's strange that it is the default behaviour for UpdateModel.
mipi
+1  A: 

I've made Action attribute and ValueProvider for solving this problem. Named it GetValuesAttribute and StrictValueProviderDictionary resp. You can set the source Form, RouteData or QueryString. For example if you want that ValueProvider would search for values in Form and maybe QueryString write the following above your method or controller:

[GetValues(ValueSource.Form | ValueSource.QueryString)]
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(FormCollection formValues)
{
   ...
   UpdateModel(myModel); //model will be updated from mentioned sources

The source code is placed here: http://codepaste.net/2kpzct. The only problem is that it won't work if you'll want to get populated model as a parameter.

mipi
Neat stuff. Thanks a lot.
Arnis L.