0
votes

I've been struggling with creating and editing a model that contains another model as part of its model.

SubCategory contains Category as a variable because it needs to be able to reference back to it.

Now I solved the create part by exposing the view to a list of Categories, via the ViewBag and binding it to a dropdown (model => model.Category.ID, new SelectBox .. etc):

    public ActionResult Create()
    {
        ViewBag.Categories = db.Category.ToList();
        return View();
    } 

    [HttpPost]
    public ActionResult Create(SubCategory subcategory)
    {
        subcategory.Category = db.Category.Single(x => x.ID == subcategory.Category.ID);
        if (ModelState.IsValid)
        {
            db.SubCategory.Add(subcategory);
            db.SaveChanges();
            return RedirectToAction("Index");  
        }

        return View(subcategory);
    }

As you can see, I had to do a quickfix above to get the subcategory to correctly bind to the category (In the DB Subcategory as a column with CategoryID).

Now when i attempted the same for the Edit function it did not work. Below is my Edit code:

    public ActionResult Edit(int id)
    {
        ViewBag.Categories = db.Category.ToList();
        SubCategory subcategory = db.SubCategory.Find(id);
        return View(subcategory);
    }

    [HttpPost]
    public ActionResult Edit(SubCategory subcategory)
    {
        subcategory.Category = db.Category.Single(x => x.ID == subcategory.Category.ID);
        if (ModelState.IsValid)
        {
            UpdateModel(subcategory);
            db.Entry(subcategory).State = EntityState.Modified;
            db.SaveChanges();
            return RedirectToAction("Index");
        }
        return View(subcategory);
    }

As you can see, same method as the create, Using a break-point i see that subcategory.Category is correctly populated, but for some reason it does not save the state and after the page has reloaded the reference to CategoryID is unchanged.

Any ideas or help will be much appriciated, thanks.

UPDATE

   [HttpPost]
    public ActionResult Edit(SubCategory subcategory)
    {
        subcategory.Category = db.Category.Single(x => x.ID == subcategory.Category.ID);
        if (ModelState.IsValid)
        {
            db.SubCategory.Single(x => x.ID == subcategory.ID).Name = subcategory.Name;
            db.SubCategory.Single(x => x.ID == subcategory.ID).Category = db.Category.Single(x => x.ID == subcategory.Category.ID);
            db.SaveChanges();
            return RedirectToAction("Index");
        }
        return View(subcategory);
    }

This works but makes my eyes bleed, so if you have a better suggestion :):

3

3 Answers

1
votes

I normally have one action to handel both my Creates and Edit saves like below

public ActionResult Save(SubCategory subcategory)
        {

            if (ModelState.IsValid)
            {
                if (subcategory.id> 0)
                {
                    SubCategory orig = db.SubCategory.Single(x => x.ID == subcategory.id);
                    if (TryUpdateModel<SubCategory>(orig))
                    {
                        db.Save();
                    }
                }
                else
                {
                    db.Add(subcategory);
                    db.Save();
                }

            }


            return View(subcategory);
        }

I also expose int virtual categoryID on SubCategory

1
votes

Normally in MVC you don't bind the Category property of your SubCategory like the way you shown. Instead you bind to scalar property CategoryId

public class SubCategory
{
   public string CategoryId{get;set;}
   public virtual Category Category{get;set;}
   //other properties
}

Then in your view

@Html.DropDownFor(model => model.CategoryID, new SelectBox .. etc)

This way you dont have to send an extra round-trip to the database to fetch the Category of the SubCategory object.

0
votes

I think your problem is that in your Edit action method the model is not bound to the data source.

After you call UpdateModel (Is it necessary to call it?) you have to call

db.Subcategories.Attach(subcategory);

This way the EF will know that there's a modification, otherwise the SubCategory object is not attached to the db object and when you save the changes it won't know that instance.

Hope that helps.