4

I'm working on an ASP.NET MVC2 app. I've come to realize a very surprising, yet amazing thing that MVC does behind the scenes having to do with the ModelState and model binding. I have a ViewModel which has a whole bunch of data - some fields being part of a form while others are simply part of the UI. On HttpPost, my Action method uses the DefaultModelBinder which attempts to bind the whole model, but only fields which were part of the form are successfully deserialized - all others remain null. That's fine and understandable. If the ModelState is invalid, I need to refresh the model from the db and bind those particular form fields before returning to the same edit view to display those associated ModelState validation errors.

Here's where my amazement and curiosity comes. It was my assumption that in order for me to bind the form fields with the refreshed model, I needed to make a call to either UpdateModel() or TryUpdateModel<>(), passing in the newly refreshed model. For example:

[HttpPost]
public ActionResult EditDetail(EditDetailItemModel model)
{
    if (model.IsValid)
    {
        // Save the results to the db

        return RedirectToAction(...)
    }

    // Can't simply "return View(model)". Not all fields in EditDetailItemModel
    // were part of the form - thus they returned null. Have to refresh
    // model from the db.

    var refreshedModel = RefreshModelFromDB();

    // Is this line necessary?????
    TryUpdateModel<EditDetailItemModel>(refreshedModel);

    return View(refreshedModel);
}

But, what I found was that if I simply returned refreshedModel to the view WITHOUT making a call to TryUpdateModel<>(), the refreshed model was automatically bound with the form field values posted!! Hence, the TryUpdateModel<>() is not needed here!

The only way I can make any sense of it is that since the ModelState is in an invalid state, once I returned the view with the refreshed model, the "MVC rendering engine" looped through the ModelState errors and bound those property values with my refreshed model. That is simply AWESOME! But, I want proof as to this assumption. I can't find documentation regarding this anywhere on the web. Can anyone either confirm my hypothesis of WHY/HOW this AWESOME auto binding behavior is occuring and/or educate me as to why/how it's happening, hopefully backed up with some online documentation links so I understand more fully what's going on under the covers?

tbehunin
  • 1,043
  • 1
  • 12
  • 24

1 Answers1

0
public ActionResult EditDetail(EditDetailItemModel model)

That line will perform model binding. Think of ActionMethod parameters as always being populated by a call to UpdateModel.

You are not seeing refreshedModel's values in the view, you are seeing the ModelState entries and values from EditDetailItemModel.

John Farrell
  • 24,673
  • 10
  • 77
  • 110
  • Right - the values I see in the view once I "post back" are the ModelState entries. BUT with a difference - the "other" non-form properties are NOT NULL! If I didn't refresh the model before posting back to the view, those other properties would still be null. But because I refreshed the model, when I set a breakpoint in the view, I can see that, not only are the non-form properties correctly restored, but the form properties were reverted back to the values the user entered in the form - EXACTLY what I wanted to happen. What I'm looking for is some docs which confirm that functionality. – tbehunin Nov 02 '10 at 16:01
  • 1
    Nothing online (that I can find) talks about having to "refresh" the model before returning to display the validation errors, which I think is strange. It seems like a pretty standard thing to do but no examples I've found attempt to do this. Looking at the code in my question above, if I didn't make the call to TryUpdateModel<>(), it would appear that the "refreshModel" (whose values are straight from the db) would be the values displayed to the user - not the values they entered before POSTing. That is AWESOME. Just would ease my conscience if I could read this expected functionality online. – tbehunin Nov 02 '10 at 16:14
  • @tbehunin, I don't understand what you are looking for. When you call `return View(refreshedModel);` you are passing that viewmodel to the view. Thats just how things work. TryUpdateModel will create ModelState entries for that refreshedModel and any htmlhelpers will use those first before looking into the model. – John Farrell Nov 02 '10 at 17:05