views:

595

answers:

3

I am getting the following error when trying to attach an object that is already attached to a given context via context.AttachTo(...):

An object with the same key already exists in the ObjectStateManager. The ObjectStateManager cannot track multiple objects with the same key.

Is there a way of achieving something along the lines of:

context.IsAttachedTo(...)

Cheers!

Edit:

The extension method Jason outlined is close, but it doesn't work for my situation.

I am trying to do some work using the method outlined in the answer to another question:

http://stackoverflow.com/questions/1601350/how-do-i-delete-one-or-more-rows-from-my-table-using-linq-to-entities-without-r

My code looks a bit like this:

var user = new User() { Id = 1 };
context.AttachTo("Users", user);
comment.User = user;
context.SaveChanges();

This works fine, except when I do something else for that user where I use the same method and try to attach a dummy User object. This fails because I have previously attached that dummy user object. How can I check for this?

+3  A: 

Try this extension method (this is untested and off-the-cuff):

public static bool IsAttachedTo(this ObjectContext context, object entity) {
    if(entity == null) {
        throw new ArgumentNullException("entity");
    }
    ObjectStateEntry entry;
    if(context.ObjectStateManager.TryGetObjectStateEntry(entity, out entry)) {
        return (entry.State != EntityState.Detached);
    }
    return false;
}

Given the situation that you describe in your edit, you might need to use the following overload that accepts an EntityKey instead of an object:

public static bool IsAttachedTo(this ObjectContext, EntityKey key) {
    if(key == null) {
        throw new ArgumentNullException("key");
    }
    ObjectStateEntry entry;
    if(context.ObjectStateManager.TryGetObjectStateEntry(key, out entry)) {
        return (entry.State != EntityState.Detached);
    }
    return false;
}

To build an EntityKey in your situation, use the following as a guideline:

EntityKey key = new EntityKey("MyEntities.User", "Id", 1);

You can get the EntityKey from an existing instance of User by using the property User.EntityKey (from interface IEntityWithKey).

Jason
This is very good, but it doesn't work for me in my situation... I'll update the question with details. p.s. you want bool not boolean, and static, but other than that pretty awesome extension method!
joshcomley
@joshcomley: I think that you can address using an overload of `TryGetObjectStateEntry` that accepts an `EntityKey` instead of an `object`. I have edited accordingly. Let me know if this doesn't help and we'll go back to the drawing board.
Jason
Ah just saw this - I was working on a solution outlined in an answer I just posted. +1 for your help and pointers!!
joshcomley
+1  A: 

Using the entity key of the object you are trying to check:

var entry = context.ObjectStateManager.GetObjectStateEntry("EntityKey");
if (entry.State == EntityState.Detached)
{
  // Do Something
}

Kindness,

Dan

Daniel Elliott
+2  A: 

Here's what I ended up with, which works very nicely:

public static void AttachToOrGet<T>(this ObjectContext context, string entitySetName, ref T entity)
 where T : IEntityWithKey
{
 ObjectStateEntry entry;
 // Track whether we need to perform an attach
 bool attach = false;
 if (
  context.ObjectStateManager.TryGetObjectStateEntry
   (
    context.CreateEntityKey(entitySetName, entity),
    out entry
   )
  )
 {
  // Re-attach if necessary
  attach = entry.State == EntityState.Detached;
  // Get the discovered entity to the ref
  entity = (T)entry.Entity;
 }
 else
 {
  // Attach for the first time
  attach = true;
 }
 if (attach)
  context.AttachTo(entitySetName, entity);
}

You can call it as follows:

User user = new User() { Id = 1 };
II.AttachToOrGet<Users>("Users", ref user);

This works very nicely because it's just like context.AttachTo(...) except you can use the ID trick I cited above each time. You end up with either the object previously attached or your own object being attached. Calling CreateEntityKey on the context makes sure it's nice and generic and will work even with composite keys with no further coding (because EF can already do that for us!).

joshcomley