2

I'm working on an edit method for a model that has a one-to-many relationship: Grid and Gridboxes. Whenever I try to edit the contents of the grid, I get this error:

Attaching an entity of type 'MyProject.Models.Gridbox' failed because another entity of the same type already has the same primary key value. This can happen when using the 'Attach' method or setting the state of an entity to 'Unchanged' or 'Modified' if any entities in the graph have conflicting key values. This may be because some entities are new and have not yet received database-generated key values. In this case use the 'Add' method or the 'Added' entity state to track the graph and then set the state of non-new entities to 'Unchanged' or 'Modified' as appropriate.

Here is my Edit method:

    [HttpPost]
    [ValidateAntiForgeryToken]
    public ActionResult Edit([Bind(Include = "GridID,Title")] Grid grid, string gridboxes)
    {
        GridboxCollection gridboxList = JsonConvert.DeserializeObject<GridboxCollection>(gridboxes);
        grid.Gridboxes = gridboxList.Gridboxes;
        UpdateGridGridboxes(gridboxList.Gridboxes, grid);

        if (ModelState.IsValid)
        {
            db.Entry(grid).State = EntityState.Modified;
            db.SaveChanges();
            return RedirectToAction("Index");
        }
        return View(grid);
    }

Here's the helper UpdateGridGridboxes method:

    private void UpdateGridGridboxes(Gridbox[] gridboxes, Grid gridToUpdate)
    {
        // If edited grid has no gridboxes (user has deleted all of them)
        if (gridboxes.Length == 0)
        {
            gridToUpdate.Gridboxes = new List<Gridbox>();
            return;
        }
        // If edited grid does have at least one gridbox
        else
        {
            gridToUpdate.Gridboxes = gridToUpdate.Gridboxes.ToList();
            foreach (var gridbox in gridboxes)
            {

                // If there is no gridboxID (if it's a new gridbox), add it to the grid's gridbox list
                if (gridbox.GridBoxID == 0)
                {
                    gridbox.GridID = gridToUpdate.GridID;
                    gridToUpdate.Gridboxes.Add(gridbox);
                    db.Entry(gridbox).State = EntityState.Added;
                    db.SaveChanges();
                }
                // If it's an existing gridbox, update its properties
                else
                {
                    var originalGridbox = db.Gridbox.Find(gridbox.GridBoxID);

                    if (originalGridbox != gridbox)
                    {
                        db.Entry(originalGridbox).CurrentValues.SetValues(gridbox);
                        db.Entry(originalGridbox).Property(x => x.GridID).IsModified = false;
                        db.Entry(originalGridbox).State = EntityState.Modified;
                        db.SaveChanges();
                    }
                    else
                    {
                        db.Entry(originalGridbox).State = EntityState.Unchanged;
                    }
                }
            }
        }
        var existingGridboxesInGrid = gridToUpdate.Gridboxes;

        // iterate through existing gridboxes and remove the ones that have been deleted from the grid
        foreach (var existingGridboxInGrid in existingGridboxesInGrid)
        {
            if (!gridboxes.Contains(existingGridboxInGrid))
            {
                gridToUpdate.Gridboxes.Remove(existingGridboxInGrid);
            }
        }
    }

My thought process goes like this:

  1. If a gridbox has been added to the grid, add it to the Grid.Gridboxes collection.
  2. If a gridbox has had its position or size changed, update those values in the database (with db.Entry(originalGridbox).CurrentValues.SetValues(gridbox).
  3. If a gridbox has been removed from the grid, remove it from the Grid.Gridboxes collection

So I can't make sense of the error above. The primary key is GridboxID.

Observation: New gridboxes are added and existing gridboxes are resized without issues. After I get the error, I can come back to the grid and the changes have been saved. The problem seems to be with existing gridboxes. It tries to re-add them to the database for some reason.

Ege Ersoz
  • 6,461
  • 8
  • 34
  • 53
  • I think that [this](https://msdn.microsoft.com/en-us/library/s6938f28.aspx) will clear things up a bit. You have to pass by reference and not by value. This way EF will know that you're talking about the same objects. – GregoryHouseMD Dec 03 '15 at 19:11
  • Pass what by reference? `Gridbox[] gridboxes` or `Grid gridToUpdate`? – Ege Ersoz Dec 03 '15 at 19:58
  • 1
    Please have a look at my answer on [ASP.NET MVC - Attaching an entity of type 'MODELNAME' failed because another entity of the same type already has the same primary key value](http://stackoverflow.com/questions/23201907/asp-net-mvc-attaching-an-entity-of-type-modelname-failed-because-another-ent/39557606#39557606). – Murat Yıldız Sep 18 '16 at 12:27

1 Answers1

0

After you deserialize you json object try attaching it to your current db context before calling update method - it is why it fails on update as it has two objects that has the same ID. The code for this would be something like this:

   db.grids.Attach(grid);

This way ef should correct track all the changes on your object.

Łukasz Trzewik
  • 1,165
  • 2
  • 11
  • 26
  • I don't get the error anymore, but now changes made to gridboxes (size, position) don't get saved. They were getting saved before. – Ege Ersoz Dec 03 '15 at 20:06
  • I can add gridboxes to the grid, but they don't get deleted when I remove them from the grid. Do I need to add each gridbox object to the dbcontext? – Ege Ersoz Dec 03 '15 at 20:13