Can EF really be this bad? Maybe…
Let’s say I have a fully loaded, disconnected object graph that looks like this:
myReport =
{Report}
{ReportEdit {User: "JohnDoe"}}
{ReportEdit {User: "JohnDoe"}}
Basically a report with 2 edits that were done by the same user.
And then I do this:
EntityContext.Attach(myReport);
InvalidOperationException: An object with the same key already exists in the ObjectStateManager. The ObjectStateManager cannot track multiple objects with the same key.
Why? Because the EF is trying to attach the {User: "JohnDoe"} entity TWICE.
This will work:
myReport =
{Report}
{ReportEdit {User: "JohnDoe"}}
EntityContext.Attach(myReport);
No problems here because the {User: "JohnDoe"} entity only appears in the object graph once.
What’s more, since you can’t control how the EF attaches an entity, there is no way to stop it from attaching the entire object graph. So really if you want to reattach a complex entity that contains more than one reference to the same entity… well, good luck.
At least that’s how it looks to me. Any comments?
UPDATE: Added sample code:
// Load the report
Report theReport;
using (var context1 = new TestEntities())
{
context1.Reports.MergeOption = MergeOption.NoTracking;
theReport = (from r in context1.Reports.Include("ReportEdits.User")
where r.Id == reportId
select r).First();
}
// theReport looks like this:
// {Report[Id=1]}
// {ReportEdit[Id=1] {User[Id=1,Name="John Doe"]}
// {ReportEdit[Id=2] {User[Id=1,Name="John Doe"]}
// Try to re-attach the report object graph
using (var context2 = new TestEntities())
{
context2.Attach(theReport); // InvalidOperationException
}
The problem is that you modified the default
MergeOption:Entities retrieved with
NoTrackingare intended for read-only use because there is no fixup; this is in the documentation forMergeOption. Because you setNoTracking, you now have two entirely separate copies of{User: "JohnDoe"}; without fixup the “duplicate” references don’t get boiled down to a single instance.Now when you try to save “both” copies of
{User: "JohnDoe"}, the first succeeds in being added to the context, but the second can’t be added because of the key violation.