Adding object to entityset is creating unwanted new DB row (many-to-many). Create relationship with existing object?
I am long time reader - first time poster - and after countless hours of research on this Entity Framework issue, I felt like I needed some help. I'm new to C# and new to programming, so please bear with me.
I have a fairly simple many-to-many relationship in my data model with Web 2.0 style "Tags". Shifts have tags and users have tags.
I am pulling a shift from a cache. When I try to make a copy of a shift, copying over the shift details and tags from the cached copy, I do the following (simplified).
Data.Shift s = new Data.Shift();
/* copy over a bunch of stuff from the cached shift object. I'll spare the boring details */
foreach (var t in shift.Tags) { //shift object is a cached object
Data.Tag dt = new Data.Tag
{
TagID = t.TagID,
Name = t.Name,
OrgID = t.OrgID,
};
s.Tags.Add(dt);
}
Even though I have explicitly set the TagID in the new Data.Tag object, on SaveChanges() a new tag is inserted into the DB, rather than just creating a relationship on the Tag that already exists in the DB. TagID is a PK identity column
When I try the following code:
foreach (var t in shift.Tags){
s.Tags.Add(t)
}
It obviously fails because the shift was cached from a different object context than the current requests' context.
Any thoughts? As far as I can tell:
- Clearing the cache is not an option for me because of performance concerns -- I recognize that this whole issue would go away if I did this work within the same object context.
- Also, pulling the Data.Tag from the DB in the current context is not an option because of perf concerns.
This seems like a really easy thing I am trying to do...
Edit Ok -- I've tried updating my code with both solutions but I am running into trouble with both. Let's try the first one:
// ctx is grabbed up here from HttpContext.Current.Items
Data.Shift s = new Data.Shift();
/* copy over a bunch of stuff from the cached shift object. I'll spare the boring details */
foreach (var t in shift.Tags) { //shift object is a cached object
Data.Tag dt = new Data.Tag
{
TagID = t.TagID,
Name = t.Name,
OrgID = t.OrgID,
};
ObjectStateEntry entry;
ctx.ObjectStateManager.TryGetObjectStateEntry(t.EntityKey, out entry);
if (entry == null || entry.State == EntityState.Detached) {
ctx.Tags.Attach(t);
}
s.Tags.Add(dt);
}
This is throwing the following error:
An entity object cannot be referenced by multiple inst开发者_如何学Cances of IEntityChangeTracker.
I am guessing that the reason I am getting is this error, is because the shift object and its tags are pulled out of a cache (obviously from different contexts). Thoughts?
It doesn't matter if you set TAgId
to existing value or not. EF is stupid - it doesn't do any Upsert / Merge for you. It doesn't check if the same entity exists and if TagId
is auto generated in the database it even throw away your value once you call SaveChanges
.
What you have to do? You must manually tell EF that Tag
is not a new entity.
Try either:
Data.Shift s = new Data.Shift();
context.Shifts.AddObject(s); // Add a new shift
foreach (var t in shift.Tags)
{ //shift object is a cached object
Data.Tag dt = new Data.Tag
{
TagID = t.TagID,
Name = t.Name,
OrgID = t.OrgID,
};
context.Tags.Attach(Tag); // Attach an existing tag
s.Tags.Add(dt);
}
context.SaveChanges();
or
Data.Shift s = new Data.Shift();
foreach (var t in shift.Tags)
{ //shift object is a cached object
Data.Tag dt = new Data.Tag
{
TagID = t.TagID,
Name = t.Name,
OrgID = t.OrgID,
};
s.Tags.Add(dt);
}
context.Shifts.AddObject(s);
// Now shift and all tags are added
// Change the state of each tag to unchanged
foreach (var tag in s.Tags)
{
context.ObjectStateManager.ChangeEntityState(tag, EntityState.Unchanged);
}
context.SaveChanges();
精彩评论