views:

65

answers:

1

I am working through sample MVC Nerdinner tutorial and using it on the AdventureWorks database. I have created an Edit action in the CategoryController to edit Product Category in AdventureWorks. The only updateable field in this table is the Name (the other fields - ID, RowGUID and UpdateDate are autogenerated). So my edit form View has only 1 field for the Name (of Product Category). My "Save" action for the edit is below: -

    [AcceptVerbs(HttpVerbs.Post)]
    public ActionResult Edit(int id, FormCollection collection){
        ProductCategory p = awRepository.GetProductCategory(id);
        try
        {
            //UpdateModel(p);
            p.Name = Request.Form["Name"];
            awRepository.Save();
            return RedirectToAction("Details", new { id = p.ProductCategoryID });
        }
        catch
        {
            foreach (var err in p.GetRuleViolations())
            {
                ModelState.AddModelError(err.PropertyName, err.ErrorMessage);
            }
            return View(p);
        }
    }

If I use the code as above, everything works as long as the Name I enter is valid (thus there is no exception). If I introduce an error (which is raised by GetRuleViolations if the Name is blank or for testing purposes is a particular "Test" string) I get a NullReferenceException (Object reference not set to an instance of an object) on this line in the View (Category/Edit.aspx) when the Edit View is redrawn (to show the user the error and allow him to correct)

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

If I update my ProductCategory using UpdateModel(p) instead of using the Request.Form variable, everything works fine; Valid data is saved and invalid data redraws the view showing the error message.

My question is: what is the difference between UpdateModel and manual updating my variable by reading the values from Request.Form collection? The Nerdinner tutorial seems to suggest that both are equivalent. So I am surprised that one works smoothly and the other raises an exception.

+1  A: 

Sounds like this:

http://forums.asp.net/p/1396019/3006051.aspx

So, for every error you add with ModelState.AddModelError() and call the View again, MVC Framework will try to find an AttemptedValue for every error it finds. Because you didn't add them, MVC will throw an exception.

Normally you don't need to add these values: AttemptedValues are automaticaly populated when you use DefaultBinding (by calling UpdateModel() or by passing the object to bind as an Action Method paramter: public ActionResult Create(FormCollection Form, YourObjectType yourObject).

Looks like the following is done automatically by UpdateModel, but not done manually by yourself?

if (Form["Name"].Trim().Length == 0)
{    
    ModelState.AddModelError("Name", "Name is required");    

    //You missed off SetModelValue?
    ModelState.SetModelValue("Name", Form.ToValueProvider()["Name"]); 
}
Meff
Yep, when I was doing things that way, I had to set the Model value as well. I've since moved to using DataAnnotations to provide most of this logic on custom view models.
Zhaph - Ben Duguid