Reputation: 11243
I've seen numerous examples of create actions in articles, books, and examples. It seem there are two prevalent styles.
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(FormCollection collection)
{
try
{
var contact = Contact.Create();
UpdateModel<Contact>(contact);
contact.Save();
return RedirectToAction("Index");
}
catch (InvalidOperationException ex)
{
return View();
}
}
And...
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create([Bind(Exclude="Id")]Contact contact)
{
try
{
contact.Save(); // ... assumes model does validation
return RedirectToAction("Index");
}
catch (Exception ex)
{
// ... have to handle model exceptions and populate ModelState errors
// ... either here or in the model's validation
return View();
}
}
I've tried both methods and both have pluses and minuses, IMO.
For example, when using the FormCollection version I have to deal with the "Id" manually in my model binder as the Bind/Exclude doesn't work here. With the typed version of the method I don't get to use the model binder at all. I like using the model binder as it lets me populate the ModelState errors without having any knowledge of the ModelState in my model's validation code.
Any insights?
Update: I answered my own question, but I wont mark it as answered for a few days in case somebody has a better answer.
Upvotes: 7
Views: 2782
Reputation: 11577
You should always accept the model as an action parameter, because it makes the method more testable.
It's far easier to pass in a model object from your unit test, than to attempt to mock out the whole HttpContext to provide data to be picked up by UpdateModel.
Even in the case where you want to update an existing entity model, you should still use this pattern by introducing a view model:
[HttpPost]
public ActionResult Edit(int id, EditContactViewModel viewModel)
{
if (ModelState.IsValid)
{
Contact contact = _db.GetContact(id)
contact.Name = viewModel.Name;
_db.SaveChanges();
return RedirectToAction("Index");
}
return View(viewModel);
}
Upvotes: 1
Reputation: 2346
Use UpdateModel
when you want to update an already present model object, which you may get from database or you want to instantiate the model object in some specific way
eg:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult EditEmployee(int id, FormCollection collection)
{
try
{
Contact contact = repository.getContact(id);
UpdateModel(contact, collection.ToValueProvider());
repository.save();
return RedirectToAction("Index");
}
catch
{
//Handle
return View();
}
}
If you dont have the above requirements then put it as action parameter.
Upvotes: 3
Reputation: 28153
There are no correct or incorrect styles. If DefaultModelBinder suits your needs - always use strongly-typed version. If you can not use DefaultModelBinder - choose between creating a custom ModelBinder or using FormCollection
Action parameter.
Upvotes: 0
Reputation: 11243
Well, after some dinking around I discovered what should have been obvious: passing the model as the parameter merely causes the default model binder to get used behind the scenes. Since this was my biggest reason not to use the strongly-typed version, then I guess there is no reason not to now.
Also, my code should look like this:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create([Bind(Exclude="Id")]Contact contact)
{
if(!ModelState.IsValid)
return View();
contact.Save();
return RedirectToAction("Index");
}
Upvotes: 2