I have a class with two references to the same class. When updating the main class, I may also update the referenced class. When I have two references to the same (modified) object, I get an InvalidOperationException:
Attaching an entity of type 'ns.entity' 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.
Simple example:
public class Example {
public int OldestFriendId {get; set;}
public int BestFriendId {get; set;}
public virtual Friend Oldest {get; set; }
public virtual Friend Best {get; set; }
}
If while updating Example, I want to update the Middle name of my Oldest/Best friend, it works as long as they aren't the same. But if they are the same, then I get the above exception.
I can't figure out how to get this to work. I've tried setting references to null, saving them independently of the parent class, setting all references in them to null (EF is automatically creating two list of Examples in Friend).
How can I save an object that has changed when there are multiple references to it?
UPDATE: not yet working the way I want, but I have had some progress after removing the list of Examples from Friend. Also, the update is the result of a POST. Still investigating...
As sample code was asked for...this is from a post on a web app, no change was actually made
public ActionResult SaveEdit(int id, [Bind(Include = "OldestFriendId, BestFrinedId, Oldest, Best")] Example example)
{
if (ModelState.IsValid)
{
using (((WindowsIdentity)ControllerContext.HttpContext.User.Identity).Impersonate())
{
using (var _db = new exampleEntities())
{
//example.Best= example.Oldest; // this line would allow the update to work.
//next line is where the exception occurs
_db.Entry(example).State = EntityState.Modified;
_db.SaveChanges();
}
}
}
}
The EditorFor template:
@model Testing.Friend
<div class="col-md-10">
@Html.HiddenFor(model => model.FriendId)
@Html.EditorFor(model => model.FirstName)
@Html.EditorFor(model => model.LastName)
</div>
The Edit view for Example
@model Testing.Example
@using (Html.BeginForm())
{
@Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Example</h4>
<hr />
@Html.ValidationSummary(true, "", new { @class = "text-danger" })
@Html.HiddenFor(model => model.ExampleId)
<div class="form-group">
@Html.LabelFor(model => model.OldestFriendId, "OldestFriendId", htmlAttributes: new { @class = "control-label col-md-2" })
@Html.HiddenFor(model => model.OldestFriendId)
@Html.EditorFor(model => model.Oldest)
</div>
<div class="form-group">
@Html.LabelFor(model => model.BestFriendId, "BestFriendId", htmlAttributes: new { @class = "control-label col-md-2" })
@Html.HiddenFor(model => model.BestFriendId)
@Html.EditorFor(model=> model.Best)
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</div>
</div>
}
EDIT
The most likely cause is because when you retrieve the object back, it deserializes the 2 friends as 2 completely different objects (even when they are the same). Same problem as below, but rather than EF deserializing into 2 objects, ASP.NET MVC is doing it.
What you will have to do is something like the following:
Best = OldestSaveChanges()Original Answer
My guess is that this is the classic problem of Include when you are retrieving the data.
When you do
Context.Examples.Include(x => x.Oldest).Include(x => x.Best).ToList()
What is happening is EF will create TWO objects of friend(Oldest and Best), even if they point to the same record. This is a known problem with include.
So when you go to save after update, EF sees them as 2 separate entities with the same key (and data) and complains.
If this is the case you have a couple of options:
Friends for the current example and then the Example without the includeIf you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With