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:
- If a gridbox has been added to the grid, add it to the Grid.Gridboxes collection.
- If a gridbox has had its position or size changed, update those values in the database (with
db.Entry(originalGridbox).CurrentValues.SetValues(gridbox)
. - 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.