0

I followed this Saving Many to Many relationship data on MVC Create view

This works for adding my many to many values.

But when I try to edit using the same code, No matter what I do, it doesn't change the many to many values.

My scenario is...

I have Menus, and Pages.

Many pages can be added to many menus.

I add the pages to the menu in question perfectly, but when editing, it results in the same values every time, even if I call Menu.Pages.Clear(); Context.SaveChanges(); It just results in the same values as before the edit on the page list associated to the menu.

[HttpPost]
    public ActionResult Edit(MenuViewModel MenuViewModel, int PageID, int ActivePosition, int OldAP)
    {
        Service.AddOrUpdateCourses(MenuViewModel.ZMenu, MenuViewModel.Pages);
        ViewBag.Message = Service.CreateOrUpdate<ZMenu>(MenuViewModel.ZMenu, PageID, ActivePosition, OldAP);

        if (ViewBag.Message.ToString() == "true")
        {
            Service.Commit();
            return RedirectToAction("Index");
        }

        ViewBag.ActivePosition = ModuleManager.AvailableModulePositions(OldAP);
        ViewBag.PageID = Service.GetPageSelectList(MenuViewModel.ZMenu.Page.ID);
        return View(MenuViewModel);
    }

PageID is the page the module (menu) is positioned onto, ActivePosition is the module position it is in, OldAP is the position it WAS in before the update. Things are pre-fixed with 'Z' because it's the general theme in the code and stops any confusion with any other functions.

Service is the Repository.

public void AddOrUpdateCourses(ZMenu ZMenu, IEnumerable<AssignedPageData> AssignedPages)
    {
        ZMenu.Pages.Clear();
        foreach (var AssignedPage in AssignedPages)
        {
            if (AssignedPage.Assigned)
            {
                var newpage = Context.ZPages.Find(AssignedPage.ID);
                Context.ZPages.Attach(newpage);
                ZMenu.Pages.Add(newpage);
            }
        }
    }

Potentially useless information... The other method used....

public string CreateOrUpdate<T>(object _Module, int PageID, int ActivePosition, int OldAP = -1)
    {
        Type type = typeof(T);
        dynamic Module = type.DynamicCast(_Module);

        // set the date, depending on if its a create or edit

        if (Module.ID == 0 || Module.ID == null)
            Module.Date = DateTime.Now;
        else
        {
            int ID = Module.ID;
            Module.Date = Context.ZModules.AsNoTracking().Single(x => x.ID == ID).Date;
        }

        Module.Type = Context.ZModuleTypes.Single(x => x.Name == type.Name.Substring(1, type.Name.Length - 1));
        Module.Page = Context.ZPages.Single(x => x.ID == PageID);
        Module.ActivePosition = ActivePosition;

        bool PositionTaken = false;//this.CheckModulePositions<T>(_Module, OldAP);
        if (PositionTaken)
        {
            return "Position " + Module.ActivePosition + " is taken, please select another";
        }
        else
        {
            try
            {
                int ID = Module.ID ?? -1;
                if (Context.ZModules.AsNoTracking().Where(x => x.ID == ID).Count() == 0)
                {
                    Context.Entry(Module).State = System.Data.EntityState.Added;
                }
                else
                {
                    Context.Entry(Module).State = System.Data.EntityState.Modified;
                }

                return "true";
            }
            catch (Exception ex)
            {
                return ex.Message;
            }
        }
    }

I have disabled the module position protection (1 module per module position) to stop that from being a potential problem, and that hadn't fixed it, just incase you were wondering why I have a small line of code commented out above.

I have debugged this, and the ZMenu entity has the correct Pages in question as the Commit() method is called, but after that the page list gets reverted to how it was before the edit. Works when creating.

Any one have any clue ?

Will supply more information if needed.

EDIT:

Here is the ViewModel in question...

public class MenuViewModel
{
    public ZMenu ZMenu { get; set; }
    public virtual ICollection<AssignedPageData> Pages { get; set; }

    public MenuViewModel(ZMenu ZMenu)
    {
        this.ZMenu = ZMenu;
    }

    public MenuViewModel()
    {

    }
}

EDIT 2:

Due to the comment below, and some continued work, the code is now as follows, in what seems to be the problem area, I have now encountered an error, but I am in the dark on how I would fix the error.

Store update, insert, or delete statement affected an unexpected number of rows (0). Entities may have been modified or deleted since entities were loaded. Refresh ObjectStateManager entries.

public void AddOrUpdateCourses(ZMenu ZMenu, IEnumerable<AssignedPageData> AssignedPages)
    {
        Context.ZMenuModules.Attach(ZMenu);
        HashSet<int> CurrentPages = new HashSet<int>
        (ZMenu.Pages.Select(i => i.ID));

        foreach (var AssignedPage in AssignedPages)
        {
            ZPage newpage = Context.ZPages.Find(AssignedPage.ID);
            Context.ZPages.Attach(newpage);
            if (AssignedPage.Assigned)
            {
                if (!CurrentPages.Contains(AssignedPage.ID))
                {
                    ZMenu.Pages.Add(newpage);
                }
            }
            else
            {
                if (CurrentPages.Contains(AssignedPage.ID))
                {
                    ZMenu.Pages.Remove(newpage);
                }
            }
        }
    }

The error happens when the Context.SaveChanges() is called.

EDIT 3:

As you can see this is partially answered now.

Can anyone tell me how I would refresh the ZMenu.Pages value without causing a multiple objects being tracked error.

Community
  • 1
  • 1
GTWelsh
  • 93
  • 2
  • 7
  • Have you tried attaching the ZMenu. You may need to ensure that the ZMenu is being change tracked as well. In my experience I have needed to attach all the objects in the chain. – Jim Aug 31 '12 at 04:48

3 Answers3

0

Found the problem.

Upon debugging, I noticed the ZMenu in question had no Pages in the list at all, even though it has one, even if the edit doesn't work. So this is wrong. (Before the edit one existed).

So I used the ID of the Model passed to the HttpPost Edit Action to "find" the ZMenu from the Context and used this throughout, instead of my previous "ViewModel.ZMenu".

This made the ZMenu have its 1 Page that it should of had, this must fix any issues in the program and made the edit successful, I'm very happy as this has bugged me for ages.

GTWelsh
  • 93
  • 2
  • 7
0

Ultimately fixed it by following the Microsoft tutorial.

I suggest anyone looking at the link at the top of the question follow this instead if you run into problems, as it's the same situation.

http://www.asp.net/mvc/tutorials/getting-started-with-ef-using-mvc/updating-related-data-with-the-entity-framework-in-an-asp-net-mvc-application

(the bottom most screenshot is what I was trying to do)

GTWelsh
  • 93
  • 2
  • 7
0

I wrote some blog posts that set out in detail how to Create / Update / Delete records in a many to many scenario using EF Code First. There's a full solution attached to the post also, so hopefully you or someone else reading this will find it useful. This is the third and final part:

Saving many to many data in MVC4

Ciarán Bruen
  • 5,221
  • 13
  • 59
  • 69