What Does UpdateModel() Do?
Asked Answered
E

3

11

In layman's terms, what does UpdateModel() do, as well as TryUpdateModel()? I can't seem to find (on SO or the web) any clear explanation of what it actually does (in clear terms), just people having problems using it.

VisualStudio's intellisense is not helping me either. The reason why I ask is because, let's say, if I have this in my controller:

[HttpPost]
public ActionResult Index( UserViewModel vm, FormCollection form)
{    
  var statesCheckBoxes = form["StatesList"];       

  vm.BA.StatesTraveledTo = statesCheckBoxes.Split(',').ToList<string>();

  return View(vm);
}

Aren't I already updating my model by setting vm.BA.StatesTraveledTo ? Why do I need to run UpdateModel? Also, when I actually try to do the following:

[HttpPost]
public ActionResult Index( UserViewModel vm, FormCollection form)
{    
  var statesCheckBoxes = form["StatesList"];       

  vm.BA.StatesTraveledTo = statesCheckBoxes.Split(',').ToList<string>();

  UpdateModel(vm); // IS THIS REDUNDANT TO THE PREVIOUS LINE?

  return View(vm);
}

Nothing seems to happen in that when I inspect the value of the ModelState (after I run UpdateModel() ), I don't see anything indicating that anything has changed. I don't see a new key in the ModelState dictionary.

Really confused. Thanks!

Edit:

Posting the source code for the ViewModel and Model classes:

public class UserViewModel
{
  public BankAccount BA { get; set; }
}

public class BankAccount
{
  public Person User { get; set; }
  public List<string> StatesTraveledTo { get; set; }
}

public class Person
{
  public string FirstName { get; set; }
  public string LastName { get; set; }
  public int Age { get; set; }
}
Esque answered 22/12, 2011 at 6:37 Comment(4)
Here is the source code for it: aspnet.codeplex.com/SourceControl/changeset/view/72551#266451 It's pretty simple,, just creates a ModelBindingContext and binds itSaskatoon
Also, it is somewhat rare to pass the same object that you received in as input in an action into the view, it happens but it's rare and this does not seem like one of those uses. Usually you take as input an object that represents the data posted and then create a separate model for the viewSaskatoon
Thanks. The reason why I'm passing the model back to the view is for validation purposes. So in case the validation fails, I want the model and its values to be passed to the view so that the form fields get repopulated and an error message is shown. Sorry, I guess with the code I showed, it may not look like a practical example.Esque
I see, that makes more sense. I still would not use a FormCollection, I would create a dedicated class for all the values I expect to be passed in.Saskatoon
U
7

what happens when you write

public ActionResult Index( UserViewModel vm)
{    }

and when you inspect in the ActionResult you find that vm contains values that you posted from the view. it is because mvc directs the modelbinder to extract values from different sources (form collection, route values, querystring etc) and populate values of your model. But for this to happen your form keys must match the name of properties in your model and if that is the case your model is populated correctly. now we come to the actual question: what does UpdateModel do? simple answer is nothing but model binding. The difference is only that you call it explicitly. The above ActionResult can be rewritten like using UpdateModel

Public ActionResult Index ()
{
   UserViewModel vm = new UserViewModel();
   UpdateModel(vm);// it will do same thing that was previously handled automatically by mvc
}

Now, what was not handled by automatic model binding will not be handled by explicit model binding as well because its not the problem with model binder its the problem with your html. with nested view models like yours, the form field names must be carefully crafted so mvc can correctly inject it to your model without you having to write something like

vm.BA.StatesTraveledTo = statesCheckBoxes.Split(',').ToList<string>(); 

and if you don't want to do such thing check this google search

Utile answered 22/12, 2011 at 11:8 Comment(0)
S
2

Here is the source code for it: http://aspnet.codeplex.com/SourceControl/changeset/view/72551#266451

It's pretty simple,

    protected internal bool TryUpdateModel<TModel>(TModel model, string prefix, string[] includeProperties, string[] excludeProperties, IDictionary<string, ValueProviderResult> valueProvider) where TModel : class {
        if (model == null) {
            throw new ArgumentNullException("model");
        }
        if (valueProvider == null) {
            throw new ArgumentNullException("valueProvider");
        }

        Predicate<string> propertyFilter = propertyName => BindAttribute.IsPropertyAllowed(propertyName, includeProperties, excludeProperties);
    IModelBinder binder = Binders.GetBinder(typeof(TModel));

    ModelBindingContext bindingContext = new ModelBindingContext() {
        Model = model,
        ModelName = prefix,
        ModelState = ModelState,
        ModelType = typeof(TModel),
        PropertyFilter = propertyFilter,
        ValueProvider = valueProvider
    };
    binder.BindModel(ControllerContext, bindingContext);
    return ModelState.IsValid;
}

This just creates a ModelBindingContext and binds it. I believe it already happens by default before your action is called. It is rare to ever have to call it manually.

Just a guess here but you might be getting strange results because you're doing things an atypical way. Your action's signature:

public ActionResult Index( UserViewModel vm, FormCollection form)

takes a UserViewModel AND a FormCollection. Usually people do one or the other (actually FormCollection is pretty rare nowadays). Again, I'm going off memory here but I would guess that UpdateModel does nothing because those values are already bound. If it is empty then maybe it is because FormCollection receives (binds) all of your submittd values and none get left for the viewmodel to bind to.

Saskatoon answered 22/12, 2011 at 6:57 Comment(3)
Thanks. Pardon my noobness- you're saying that UpdateModel() gets run by default before my action so there's no need to run explicitly from the action?Esque
The reason why I use the FormCollection is because the default model binder (used for the ViewModel) cannot bind a set of checkboxes I have on the view against a List property I have in a BankAccount object inside the UserViewModel object. Check out the source code I just added to the question. To correctly map that deep nested property, I grab it directly from the FormCollection. The other way of not using the FormCollection object, that I know of, is creating a custom model binder, but I don't want to make it more complicated than it is, or am I missing something?Esque
Yeah, just create a class for the other values that you want with the appropriate types for the checkboxes. If it can bind it to FormCollection it should be able to bind it to a static type. Also you could just grab stuff out of the Request object.Saskatoon
D
0

Update model is basically used to update new values in your existing Model. you don't need to assign value explicitly.

Demp answered 22/12, 2011 at 7:0 Comment(1)
You're saying that I don't have to do this than... vm.BA.StatesTraveledTo = statesCheckBoxes.Split(',').ToList<string>(); ??? If I don't have to do that, then what's the syntax than if I wanted to use just the method UpdateModel()?Esque

© 2022 - 2024 — McMap. All rights reserved.