无法附加分离的实体(具有相同密钥的实体已在上下文中)

时间:2014-03-30 13:27:37

标签: c# entity-framework

我正在使用实体框架6,Code First方法。我试着用一段简单的代码来表达我的问题:

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
    }
}

例外消息是:附加类型' MyProgram.MyEntity'的实体。失败,因为同一类型的另一个实体已具有相同的主键值。

从我在MSDN上阅读的内容来看,这是预期的行为。但我想要的最后一行是首先检查是否有一个具有相同键的实体已经附加到上下文;如果是,请改为使用它,只有将我的实体附加到上下文中。

但我没有找到办法。 ObjectContext实例上有许多实用方法(例如GetObjectByKey)。我不能全部测试它们因为它们最终都需要一个qualifiedEntitySetName,而我在真正的imlpementation中没有任何东西,因为这个方法应该是一个抽象类,它应该适用于所有实体类型。调用Db.Entity(this)是没用的,没有EntityKey会有EntitySetName。

所以这一切都变得非常复杂。按照我的意思,我只想检查对象是否已经在"缓存" (上下文),使用它,否则使用我的对象并将其附加到此上下文。

要清楚,我首先从TreeNode.Tag中获得了一个分离的对象,我只想再次使用它,或者它是不可能的;如果在上下文中已经有一个),请改用它。也许我错过了EF6的一些重要概念,我刚刚开始使用EF。

2 个答案:

答案 0 :(得分:0)

实体可以分为五个阶段之一:添加,不变,修改,删除,分离。

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();
    }
}

由于EF不知道哪些属性与数据库中的属性不同,因此它将全部更新。

答案 1 :(得分:0)

我找到了解决方案。正如我猜想的那样ObjectContext.GetObjectByKey方法做了我需要的,但首先我需要构造qualifiedEntitySetName,我找到了一种方法。有点麻烦(使用MyDbContext的反射,迭代属性),但不能解决我所做的所有问题。以防万一,这是我的解决方案代码补丁:

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);
}