views:

74

answers:

4

If I have a simple model like this:

Model:

  public class AnimalModel {
       public string Species {get;set;}
  }

View:

  <%= Html.TextBox("Species") %>

ActionMethod (POST):

  AnimalModel model = new AnimalModel();

  // update model 
  UpdateModel(model);   // updates ViewData.ModeState

  // correct spelling
  if (model.Species == "catt") {
      model.Species = "cat";
  }

  // return view
  return View(model);

So the action method on a POST is supposed to correct the spelling of 'catt' to 'cat'.

The problem is that the source code for 'TextBox' will take the value from ViewData.ModelState if it finds a 'Species' value in there. This means that my correct value is not updated on a postback because 'catt' takes priority.

So I thought - ah well I'll just make it explicit like this :

  <%= Html.TextBox("Species", Model.Species) %>

Surprisingly this doesnt work and STILL takes the value from ViewData.ModelState. I'm pretty sure this is a bug in MVC because looking at the source code for this overload i find this overload. It clearly indicates that the 'useViewData' parameter is supposed to be false if 'value' is provided (which I'm doing explicitly above) :

  public static string TextBox(this HtmlHelper htmlHelper, string name, object value, IDictionary<string, object> htmlAttributes) {
        return InputHelper(htmlHelper, InputType.Text, name, value, 
               (value == null) /* useViewData */, false /* isChecked */, true /* setId */, true /* isExplicitValue */, htmlAttributes);
    }

Unfortunatley though the 'InputHelper' method doesn't respect this property for a TextBox and still looks first in the ViewDataDictionary. It finds 'catt' in this dictionary because it came directly from the POST data.

I've found a solution when a UserControl is used, which passes in NULL for ViewDataDictionary.

 <% Html.RenderPartial("AnimalControl", Model, null); %>

What is the solution to this? I'm sure this is documented somewhere but i cant seem to find it. I don't want to have to clear out ViewDataDictionary manually but i see no other solution.

A: 

I'm glad I put aside model binding and built-in HTML helpers since the beginning.

You can either fix/override the helper you need or build your own. I don't see what else can be done in this situation.

User
me either. i just spent a couple hours on this when working on something more complicated. eventually found out it was something low level like this. very frustrating!
Simon_Weaver
+1  A: 

It does appear to be a bug in MVC, from the way you described it, but...

If you have Species in the Model, why do you also have it in the ViewData Dictionary?


Consider checking out this posting in the NerdDinner Tutorial:

http://nerddinnerbook.s3.amazonaws.com/Part6.htm

Go down to where it says, "Using a ViewModel Pattern" and review that.

Robert Harvey
thats the crux of the problem. i wish it wasnt there. but MVC puts it there since it is being 'roundtripped' from the page
Simon_Weaver
The only way it could be there is if you're putting it there. Why aren't you round-tripping the model?
Robert Harvey
If you inherit your page from your model, you can bring back a model object into your controller, and work with that, instead of ViewState.
Robert Harvey
i think i confused us both. its actually in ViewData.ModelState["Species"] and not ViewData["Species"]. the TextBox helper is taking it from ModelState. ModelState receives these values when I do UpdateModel(model)
Simon_Weaver
A: 

My best solution so far (since I'm not using ViewData or ModelState explicitly in my view) :

        ViewData.ModelState.Clear();
        return View(model);

Can someone PLEASE find a better solution. This stinks!

Simon_Weaver
Consider checking out this posting in the NerdDinner Tutorial:http://nerddinnerbook.s3.amazonaws.com/Part6.htmGo down to where it says, "Using a ViewModel Pattern" and review that.
Robert Harvey
thanks for the link. this is what i'm doing with my AnimalModel class. unfortunately they dont address complex scenarios like this - although it did get me to finally get round to printing out the nerd dinner chapter. i'm sure i'll learn something. and fortunately my printer will auto-duplex so its only 50 sheets of paper.
Simon_Weaver
A: 

Try the code below to actually modify the "state" of the model. I know you'd expect it to work when you put <%= Html.TextBox("Species", Model.Species) %> there, but because of the reasons you explained, it doesn't work.

ModelState["Species"].Value =
            new ValueProviderResult("cat", "catt", CultureInfo.CurrentCulture);
çağdaş