Aleksei Chepovoi
Aleksei Chepovoi

Reputation: 3955

How to implement "Edit" method for complex model in asp.net MVC 3

In my mvc 3 web store app I have this model: Items, Products, Shipping, Users tables, where 1 Item contains 1 Product, 1 User, and 1 Shipping. I need to somehow create and edit items. With creating there are not many problems cause I can pass the "fields to fill" as parameters to Crete(Post) method. With editing there is one problem - Null Reference Exception occures. This is my code:
(controller):
I pass model (of type Item) to Edit Post method as a parameter and get product and shipping, that, I hope), I filled with values.

    [HttpGet]
    public ViewResult Edit(int itemId)
    {
        Item target = _itemsRepository.GetItem(itemId);

        return View(target);
    }

    [HttpPost]
    public ActionResult Edit(Item model)
    {
        Product p = model.Product;
        Shipping s = model.Shipping;

        // here goes some validation stuff

        if (ModelState.IsValid)
        {
            _productRepository.UpdateProduct(p);
            _shippingRepository.UpdateShipping(s);
            return RedirectToAction("Content");
        }
        return View();
    }    

Strongly typed view (where I fill the form):

@model WebStore.WebStoreModels.Item

@using (Html.BeginForm())
{
@Html.ValidationSummary(true)

<fieldset>
    <legend>Product</legend>
    @Html.HiddenFor(i => i.ItemId)

    <p>Name: </p>
    <p>
        @Html.EditorFor(i => i.Product.Name)
        @Html.ValidationMessageFor(i => i.Product.Name)
    </p>
   // and so on
</fieldset>

<fieldset>
    <legend>Shipping</legend>

    <p>Cost: </p>
    <p>
        @Html.EditorFor(i => i.Shipping.Cost)
        @Html.ValidationMessageFor(i => i.Shipping.Cost)
    </p>        
    // so on
</fieldset> 
<p>
    <input type="submit" value="Save"/>
</p>

}

And when I fill the form and click "safe" button the NullReferenceException occures in the ProductRepository class in UpdateProduct() method:

public class ProductRepository : IProductRepository
{
    private WebStoreDataContext _dataContext;

    public ProductRepository(WebStoreDataContext dataContext)
    {
        _dataContext = dataContext;
    }

    public Product GetProduct(int productId)
    {
        return _dataContext.Products.SingleOrDefault(p => p.ProductId == productId);
    }
    public void UpdateProduct(Product p)
    {
        var dbProduct = GetProduct(p.ProductId);
        dbProduct.Name = p.Name;   // here the exception occures
        dbProduct.Description = p.Description;
        dbProduct.Price = p.Price;
        dbProduct.Category = p.Category;
        dbProduct.SubCategory = p.SubCategory;
        _dataContext.SubmitChanges();
    }

Seems like I can't use this assignment:

Product p = model.Product;

I also tried: (in view and then assign it to Product in Edit(Post) method)

TempData["product"] = @Model.Product;  

And also: (in view)

@Html.Hidden("product", @Model.Product)  

And pass it as parameters to Edit(Post) method:

[HttpGet]
public ViewResult Edit(Product p, Shipping s)  

I think the problem is associated with model.
Any help would be great, Sorry for so much code)

Upvotes: 1

Views: 1995

Answers (1)

Dmitry Efimenko
Dmitry Efimenko

Reputation: 11188

You need to add hidden ProductId input inside of the form in your View:

@using (Html.BeginForm())
{
    @Html.HiddenFor(i => i.Product.ProductId)
    ...
}

The reason you need to have this line is that when you submit your form model binder looks at every input (including hidden inputs) and binds them to the properties of your Item model that you pass on [HttpPost] action. Before, when you didn't have this line, only the following properties were populated:

  • Item.ItemId
  • Item.Product.Name
  • Item.Shipping.Cost

Your model didn't have information for the value of Item.Product.ProductId. This property is int, which means that it was turning to be equal 0 when form was submitted. Inside your _productRepository.UpdateProduct(p); method you are trying to get a product by Id and obviously it cannot find a product with id = 0, which is why this call was returning null resulting in a null reference exception on the next line.

Upvotes: 2

Related Questions