2

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 instances 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?

1
  • Can you show the full code up to SaveChanges? Where do you add the Shift to the context? Do you add or attach the new tags to the context explicitely? It's likely that attaching the tags to the context is missing which would explain that new tags are created, but just a guess... Commented Aug 3, 2011 at 22:17

1 Answer 1

1

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();
Sign up to request clarification or add additional context in comments.

1 Comment

Have you tried the Identity value in the StoreGeneratedPattern enumeration? That should ensure that identity values generated in the store (database) are set in the entity. msdn.microsoft.com/en-us/library/…

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.