在 EF4.1 中正确地从上下文中附加和分离实体

Posted

技术标签:

【中文标题】在 EF4.1 中正确地从上下文中附加和分离实体【英文标题】:Attaching and detaching entities from context correctly in EF4.1 【发布时间】:2011-12-02 11:33:19 【问题描述】:

我正在尝试为实体实现缓存机制。为了正确无缝地使用实体与缓存,我需要先将实体从当前上下文中分离出来,然后再将其放入缓存中,并在我从缓存中获取它时将其附加回新上下文。 (我的上下文生命周期是每个 http 请求)

要求是-

    在实体分离时,不应删除与其关联的所有导航属性(我已经填充)。 如果我愿意,我可以更新缓存的项目(因此正确地将它们附加到新的上下文很重要)。

这是我创建 EntityCache 类的尝试——(这里的 ServerCache 是将对象推送到 ASP.NET 缓存的包装类)

public static class EntityCache
    
        private static DbContext context
        
            get
            
                return (DbContext)HttpContext.Current.Items[ObjectKeys.ContextRequestItemKey];
            
        

        private static void Detach(object entity)
        
            var trackedEntity = entity as IEntityWithChangeTracker;
            trackedEntity.SetChangeTracker(null);
            ((IObjectContextAdapter)context).ObjectContext.Detach(entity);
        

        private static void Attach(object entity)
        
            ((IObjectContextAdapter)context).ObjectContext.Attach((IEntityWithKey)entity);
        

        public static void Remove(string key)
        
            ServerCache.Remove(key);
        

        public static object Get(string key)
        
            object output = ServerCache.Get(key);
            if (output != null)
                Attach(output);
            return output;
        

        public static void ShortCache(String key, object data)
        
            if (data != null)
            
                Detach(data);
                ServerCache.ShortCache(key, data);
            
        

        public static void LongCache(String key, object data)
        
            if (data != null)
            
                Detach(data);
                ServerCache.LongCache(key, data);
            
        
    

当我将一个实体放入缓存中时,它的类型是 DynamicProxy 而不是真正的类。

附加根本不起作用 - 我得到一个异常,我无法将 Dynamic_blahblah 类型的对象设置为 IEntityWithKey。

我刚刚在网上看到了这些附加和分离的示例并进行了尝试,我对这里的附加/分离方法的任何新实现持开放态度。

谢谢。

跟进问题 -

context.Entry(entity).State = EntityState.Detached;

有效,但将所有加载的导航属性设为 NULL,我们如何让它保留导航属性,而不是在脱离上下文时用 NULL 替换(或丢失)它们。

【问题讨论】:

整个问题没有问号是正常的吗? :) 更多的是——我在这里做错了什么,因此标题为“如何正确地做”,以为很清楚——但会添加一个“?”下次:) 删除演员表到IEntityWithKey,你不需要它,因为ObjectContext.Attach 接受object。如果您希望“真实”类关闭创建代理的所有内容(延迟加载、使用代理进行更改跟踪) - 包括它们可能带来的好处。我认为您不能将实体作为成员或通过代理中的方法检索,因为代理 inherits 来自实体。因此,代理IS 实体而不HAVE 实体。但是您会遇到冲突,因为您显然希望您的实体实现IEntityWithChangeTracker 我不明白的另一件事是为什么要将上下文和缓存联系得如此紧密?在某些情况下——尤其是在 Web 应用程序中,它是大多数 = 大多数 GET 请求——您将从数据库中加载根本不附加到上下文的实体(使用 NoTracking 选项),所以您不需要分离它们。另一方面,当您从缓存中检索实体时,为什么总是想将它们附加到上下文中?附加主要用于更改跟踪。但是,当您可以从缓存中提供 GET 请求时,为什么要更改跟踪或上下文呢? 谢谢,我同意我不需要每次从缓存中获取它时都附加它,只有在我需要更新缓存的实体时才需要它。但是附加性能会受到影响吗?为什么我不应该每次都附上?同样对于您的第一条评论,我确实需要延迟加载、更改跟踪等,并且不想关闭“创建代理的所有内容” 【参考方案1】:

IEntityWithKey 是其他类型实体的接口。它适用于“大”实体。例如EntityObject 实现这个接口。这些实体不被视为 POCO,并且不受 DbContext API 的支持。

如果你想使用IEntityWithKey,你的类必须实现它——它不会自动发生。

使用DbContext API 正确附加应该是:

dbContext.Set(typeof(entity)).Attach(entity); 

这应该也可以工作:

dbContext.Entry(entity).State = EntityState.Unchanged;

使用DbContext API 正确分离应该是:

dbContext.Entry(entity).State = EntityState.Detached;

另外,你最好使用通用方法而不是object

【讨论】:

这些工作 - 但是当我分离时我的导航属性为空,有没有办法分离而不会使它们变为空,因为我在推送到缓存之前加载它们; var trackedEntity = 实体作为 IEntityWithChangeTracker; trackedEntity.SetChangeTracker(null);不成功 我认为dbContext.Set(typeof(entity)).Attach(entity); 应该是dbContext.Set(entity.GetType()).Attach(entity); 不是吗?【参考方案2】:

您的后续问题:

...我们如何让它保持导航属性而不是 当我们脱离上下文时,用 NULL 替换(或丢失)它们......

我相信在保持其导航属性的同时,不可能将对象图从上下文中分离出来。 From MSDN:

在独立关联中,关系信息不 为分离的对象维护。

尽管此声明是关于独立关联的,但这并不意味着导航属性在外键关联中维护(在模型中公开外键属性的关系)。是的,“关系信息”得到维护,因为外键属性(它们是标量属性)将是活动的并且在分离后包含正确的外键值。但是对于引用属性,相应的导航属性仍将是 null,或者对于导航集合,引用将从集合中删除。

我认为从上下文中分离完整对象图的唯一方法是完全处置上下文或在开始分离原始图之前创建图的副本。创建副本需要编写Clone 方法来复制所有属性并在图形中导航,或者使用this 之类的“技巧”将图形序列化为二进制流,然后将其反序列化回新对象。为此,实体需要可序列化。此外,引用循环(我们在实体之间使用双向导航属性时经常遇到)可能会导致麻烦。 (如果您的对象是包含对 EF 内部对象的引用并且您可能不想复制、序列化和反序列化的代理,请注意。)

在这方面Detach 不是Attach 的对应物,因为它的行为完全不同:Attach 附加整个对象图并维护导航属性。 Detach 仅分离没有相关对象的单个实体并破坏导航属性。从上面链接的同一页面:

分离只影响传递给方法的特定对象。如果 被分离的对象在对象上下文中有相关的对象,那些 对象未分离。

我可以想象这是DbContext API 没有明确的Detach 方法的主要原因(与ObjectContext 相比) - 分离被认为是一种高级功能,它的行为并不像一个可能会期待。

MSDN 提到将对象从上下文中分离的唯一原因是“以节省资源”(同样是上面的文章):

在实体框架应用程序中,您可以将对象从 对象上下文。您可以分离对象以节省资源,如 在同一对象上下文中执行重复查询会增加 对象上下文的内存要求。

我认为这种方法只是没有准备好和设计用于在对象与上下文分离后处理它们。它的主要目的是释放它们以便立即进行垃圾回收。

【讨论】:

以上是关于在 EF4.1 中正确地从上下文中附加和分离实体的主要内容,如果未能解决你的问题,请参考以下文章

为啥 EF 在分离时会删除子实体?

如何正确地将已附加到上下文的实体标记为已修改?

Spring JpaRepository - 分离和附加实体

实体框架分离实体和相关实体消失

从 JPA/EJB3 持久性上下文中分离实体

实体框架 EF4.1 - 存储过程“在容器中找不到”