2

I'm using Entity Framework 6, Code First approach. I'll try to present my problem with a simple piece of code:

public void ViewEntity(MyEntity Entity) // Want to read properties of my entity
{
    using (var Db = new MyDbContext()) 
    {
        var DummyList = Db.MyEntities.ToList(); // Iteration on this DbSet
        Db.MyEntities.Attach(Entity); // Exception
    }
}

The exception message is: Attaching an entity of type 'MyProgram.MyEntity' failed because another entity of the same type already has the same primary key value.

From what I've read on MSDN it's an expected behaviour. But what I want on that last line is to first check if there is an entity with the same key already attached to a context; if it is, use it instead, and only otherwise attach my entity to context.

But I've failed to find a way to do so. There are many utility methods on ObjectContext instance (for example GetObjectByKey). I can't test them all 'cause they all ultimately need a qualifiedEntitySetName, and I don't have any in my real imlpementation, because this method should be on an abstract class and it should work for all entity types. Calling Db.Entity(this) is no use, there is no EntityKey which would have EntitySetName.

So all of this became complex really fast. And in my terms I just want to check if the object is already in "cache" (context), use it, otherwise use my object and attach it to this context.

To be clear, I have a detached object from a TreeNode.Tag in the first place, and I just want to use it again, or if it's impossible; if there already is one in the context), use that one instead. Maybe I'm missing some crucial concepts of EF6, I'm just starting out with EF.

evilkos
  • 545
  • 10
  • 17
  • You might have a look at my answer on [ASP.NET MVC - Attaching an entity of type 'MODELNAME' failed because another entity of the same type already has the same primary key value](http://stackoverflow.com/questions/23201907/asp-net-mvc-attaching-an-entity-of-type-modelname-failed-because-another-ent/39557606#39557606). – Murat Yıldız Sep 18 '16 at 12:30

2 Answers2

0

An Entity can be in one of five stages : Added, Unchanged, Modified, Deleted, Detached.

public void ViewEntity(MyEntity entity) // Want to read properties of my entity
{
    using (var Db = new MyDbContext()) 
    {
       var DummyList = Db.MyEntities.ToList(); // Iteration on this DbSet
       // Set the Modified state of entity or you can write defensive code 
       // to check it before set the state.
       if (Db.Entry(entity).State == EntityState.Modified) {
          Db.Entry(entity).State = EntityState.Modified
       }

       // Attached it
       Db.MyEntities.Attach(Entity);

       Db.SaveChanges();
    }
}

Since EF doesn't know which properties are different from those in the database, it will update them all.

cat916
  • 1,363
  • 10
  • 18
  • It didn't help. When Db.Entry(entity).State line executes it gives this same error right away even before the "Attach" line. But I've found a solution. Not very beautiful, but it works. Just in case someone as novice as me will need it, I've posted an answer to my own question. – evilkos Mar 30 '14 at 14:30
0

I've found a solution for me. As I guessed correctly ObjectContext.GetObjectByKey method does what I need, but first I needed to construct qualifiedEntitySetName, and I found a way to do so. A tad bit cumbersome (using reflection, iterating properties of MyDbContext), but does not compare to a headache of a problem I made out of all this. Just in case, here's the patch of code that is a solution for me:

public SdsAbstractObject GetAttachedToContext()
{
    var ObjContext = (SdsDbContext.Current as IObjectContextAdapter).ObjectContext;
    var ExistingItem = ObjContext.GetObjectByKey(GetEntityKey()) as SdsAbstractObject;    
    if (ExistingItem != null)
        return ExistingItem;
    else
    {
        DbSet.Attach(this);
        return this;
    }
} 

public EntityKey GetEntityKey()
{
    string DbSetName = "";
    foreach (var Prop in typeof(SdsDbContext).GetProperties())
    {
        if (Prop.PropertyType.IsGenericType
            && Prop.PropertyType.GenericTypeArguments[0] == ObjectContext.GetObjectType(GetType()))
            DbSetName = Prop.Name;
    }
    if (String.IsNullOrWhiteSpace(DbSetName))
        return null;
    else
        return new EntityKey("SdsDbContext." + DbSetName, "Id", Id);
}
evilkos
  • 545
  • 10
  • 17