Matthew Matthew - 4 months ago 90
C# Question

Error attaching entity because of same primary key when trying to save an update

I am trying to save an update to an existing database entry but when I do I get the error:


Attaching an entity of type 'FFInfo.DAL.Location' 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.


This is my controller's code. The save method I am using is the same I use in a few other areas to update data with no problems.

[HttpPost, ValidateAntiForgeryToken]
public ActionResult EditLocation(AddEditLocationVM model, HttpPostedFileBase MapFile)
{
try
{
using (var db = new GeographyContext())
{
model.Sections = new SelectList(db.Sections.Where(s => s.ID > 1).OrderBy(s => s.Title), "ID", "Title").ToList();
model.GeographyTypes = new SelectList(db.GeographyTypes.Where(gt => gt.SectionID == model.Section).OrderBy(gt => gt.Name), "ID", "Name").ToList();
model.ParentLocations = new SelectList(db.Locations.Where(l => l.SectionID == model.Section).OrderBy(l => l.Name), "ID", "Name").ToList();


if (MapFile != null)
{
if (FileHelper.IsNotValidImage(MapFile))
{
ModelState.AddModelError("Invaalid File Type", "Images must be JPG, GIF, or PNG files.");
}
}

if (ModelState.IsValid)
{
if (MapFile != null)
{
var SectionRoute = db.Sections.Where(s => s.ID == model.Section).Select(s => s.Route).First();
model.MapFileID = FileHelper.UploadFile("Images/" + SectionRoute + "/Maps/" + MapFile.FileName.ToList(), "site", MapFile);
}

if (model.ParentLocation == 0)
{
model.ParentLocation = null;
}

var UpdatedLocation = new Location()
{
Description = model.Description,
GeographyTypeID = model.GeographyType,
ID = model.ID,
MapFileID = model.MapFileID,
Name = model.Name,
ParentLocationID = model.ParentLocation,
SectionID = model.Section
};

db.Entry(UpdatedLocation).State = EntityState.Modified;
db.SaveChanges();
ViewBag.Results = "Location information updated.";
}

return View(model);
}
}
catch (Exception ex)
{
ErrorSignal.FromCurrentContext().Raise(ex);
model.Sections = Enumerable.Empty<SelectListItem>();
ViewBag.Results = "Error updating location informaiton, please try again later.";
return View(model);
}
}


This is my Location Entity code:

public class Location
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ID { get; set; }

[Required, Index("IX_Location", 1, IsUnique = true)]
public string Name { get; set; }

[Index("IX_Location", 2, IsUnique = true)]
public Int16 SectionID { get; set; }

[Column(TypeName = "varchar(MAX)")]
public string Description { get; set; }

public Int16 GeographyTypeID { get; set; }
public int? MapFileID { get; set; }
public int? ParentLocationID { get; set; }

[ForeignKey("SectionID")]
public Section Section { get; set; }

[ForeignKey("GeographyTypeID")]
public GeographyType GeographyType { get; set; }

[ForeignKey("MapFileID")]
public File Map { get; set; }

[ForeignKey("ParentLocationID")]
public Location ParentLocation { get; set; }

public ICollection<LocationTransitionPoint> TransitionPoints { get; set; }
}


This is my first time trying to update a more complex entity like this but from what I have found on the web I can not see anything wrong.

Answer

You can not have two entities (same type) with same primary keys in memory in Entity Framework.

The problem is

model.ParentLocations = new SelectList(db.Locations.Where(l => l.SectionID == model.Section).OrderBy(l => l.Name), "ID", "Name").ToList();

in above line you somehow have loaded the Location which it's ID is model.ID

then in

var UpdatedLocation = new Location()
{
    Description = model.Description,
    GeographyTypeID = model.GeographyType,
    ID = model.ID,
    MapFileID = model.MapFileID,
    Name = model.Name,
    ParentLocationID = model.ParentLocation,
    SectionID = model.Section
};
db.Entry(UpdatedLocation).State = EntityState.Modified;

You are creating a new Location and trying to attach it to context (by setting it's state as modified), but you have loaded another Location entity with exact primary key as UpdatedLocation into memory somewhere and this cause the exception.

Try fetching the the location and then change the roperties.

var UpdateLocation = db.Locations.First(l => l.ID == model.ID);
// var UpdateLocation = db.Locations.Find(model.ID); maybe a better option
UpdatedLocation.Description = model.Description;
UpdatedLocation.GeographyTypeID = model.GeographyType;
UpdatedLocation.MapFileID = model.MapFileID;
UpdatedLocation.Name = model.Name;
UpdatedLocation.ParentLocationID = model.ParentLocation;
UpdatedLocation.SectionID = model.Section;