Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What Does UpdateModel() Do?

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; }
}
like image 257
SaltProgrammer Avatar asked Dec 22 '11 06:12

SaltProgrammer


2 Answers

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

like image 194
Muhammad Adeel Zahid Avatar answered Nov 08 '22 21:11

Muhammad Adeel Zahid


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.

like image 22
George Mauer Avatar answered Nov 08 '22 23:11

George Mauer