Entity Framework Code First - DbContext没有Detach()方法

67
我在想为什么DbContext对象中没有像ObjectContext那样的Detach方法。我只能假设这个省略是有意的,但我很难理解为什么要这样做。例如,在ASP.NET项目中将实体放入缓存时,我需要能够分离和重新附加实体。然而,由于我不能分离实体,在尝试附加先前与另一个上下文相关联的实体时,会引发“实体对象无法被IEntityChangeTracker的多个实例引用”的异常。

这里该怎么办?我是否遗漏了什么重要指导?


有人对以下问题有什么意见吗:“这里的指导是什么?我有什么遗漏吗?”就个人而言,我只对在寻找从UI返回的实例之前从上下文中读取实体的解决方案时使用Detach感兴趣。这会给我带来“ObjectStateManager中已经存在具有相同键的对象。ObjectStateManager无法跟踪具有相同键的多个对象。”的错误提示。 - R. Schreurs
4个回答

87

对于可能遇到这个问题的人,从 CTP5 开始,现在需要编写以下代码:

((IObjectContextAdapter)context).ObjectContext
为了获得 ObjectContext。

+1,简洁、优雅、一行代码。我喜欢它。另外,我想补充一下,接口可以在这里找到:System.Data.Entity.Infrastructure.IObjectContextAdapter,是的,就像Pandincus指出的那样,你也必须引用System.Data.Entity。 - dyslexicanaboko
我收到了“IObjectContextAdapter无法找到”的错误提示,建议安装Entity Framework。而Entity Framework肯定已经安装了。 - micahhoover

38

DbContext内部使用一个ObjectContext,EF团队将其作为受保护的属性提供,以防您需要降级到更低级别的API,这似乎是这里的情况,因此您可以从派生的DbContext中使用或公开所需的功能:

public class YourContext : DbContext 
{
    public void Detach(object entity) 
    {
        ObjectContext.Detach(entity);            
    }
}

你可以从你的控制器中调用这个方法来分离一个实体。

或者,你可以将其改为具有更丰富的 API:

public class YourContext : DbContext
{
    public void ChangeObjectState(object entity, EntityState entityState)
    {
        ObjectContext.ObjectStateManager.ChangeObjectState(entity, entityState);
    }
}

这是从元数据中查看 DbContext 的方式:

public class DbContext : IDisposable 
{      
    protected System.Data.Objects.ObjectContext ObjectContext { get; }
    ...
}

@Stacker 这不是答案,因为它需要在上面选择的答案中进行转换,即 DbContext 实现了 IObjectContextAdapter,从而赋予了它 ObjectContext 属性,否则无法访问。将这两个答案结合起来得到 @splite 的答案。 - eudaimos
1
ChangeObjectState 这个函数有点懒,它使用的是“带转换的线性算法”,这种算法有点...你知道的...非常糟糕。 - Jan 'splite' K.

17

EF:CF 4.1 RC1EF:CF 4.1 RTW具有相同的显式实现IObjectContextAdapter:

public static class DbContextExtensions
{
    public static void Detach(this System.Data.Entity.DbContext context, object entity)
    {
         ((System.Data.Entity.Infrastructure.IObjectContextAdapter)context).ObjectContext.Detach(entity);
    }
}

微软决定“分离(Detach)是太先进的技术,应该被隐藏”。在我看来,发明这个东西的人应该被枪毙——因为如果你添加了一个全新的实体,否则很难仅仅移除它而不提交更改到数据库(你可以使用DbEntityEntry进行操作,但这是另一回事)。

4年后的编辑:

使用EF6(我不知道怎么跳过了EF5 :) ),你不再需要detach(),因为删除新添加的条目不会像EF4那样生成delete from [table] where [Id] = 0 - 你只需要调用mySet.Remove(myFreshlyCreatedAndAddedEntity),一切都会没问题。


谁需要那个功能呢?拍照? :P - quetzalcoatl
1
@quetzalcoatl: var context = ...; var poco = new Poco(); context.Add(poco); context.Remove(poco); <--- 抛出异常,因为你需要将其 DETACH,而不是 REMOVE - 嘿,DETACH 被隐藏了... - Jan 'splite' K.
1
当时我非常生气...当你正在制作Windows应用程序时,必须有某种方法来“取消创建新的、可能不完整的条目,但保留UoW上下文”...我们摆脱了整个EF,并编写了自己的ORM:没有办法利用延迟加载(我有一堆发票,不能在单个“where InvoiceId in (xxyy)”查询中加载每个InvoiceItem - Include的工作方式与我们预期的不同),更大的存储库是如此懒惰(每次执行之前进行线性(!!!)搜索,什么鬼...),为100多个poco类型创建代理需要太长时间等等... - Jan 'splite' K.
1
嘿嘿嘿..我只是开玩笑,注意结尾的':P' :) 看起来我无意中成功地在你身上捉弄了,抱歉 :) 我完全同意你的观点。没有真正的附加/分离支持使得框架对于任何非平凡的用途都非常有限,这就是为什么它们在EF的新版本中被引入的原因。从你给出的数字来看,这似乎是一个“中等”规模的项目和数据库,不算真正的大型,但这是非常主观的,我不知道你的项目。无论如何,根据我的经验,大多数ORM / DB问题并不是来自ORM,而是来自定义不明确的业务逻辑或者DB不适合通过ORM使用。 - quetzalcoatl
我需要这个功能。我将在同一个 DbContext 上附加许多对象,有些对象需要被分离,并释放这些对象占用的内存。 - AechoLiu
显示剩余3条评论

7

我通常通过继承自DbContext的基类,并使用以下属性进行扩展:

public class MyDbContext : DbContext
{
    public ObjectContext ThisObjectContext
    {
        get
        {
            return ((IObjectContextAdapter)this).ObjectContext;
        }
    }
}

稍后,您可以使用此属性来执行各种有用的操作...例如分离 :)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接