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

时间:2011-10-06 18:15:40

标签: c# asp.net asp.net-mvc-3 entity-framework entity-framework-4.1

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

要求是 -

  1. 分离实体时,不应删除与其关联的所有导航属性(我已填充)。
  2. 如果需要,我可以更新缓存的项目(因此正确地将它们附加到新的上下文非常重要)。
  3. 这是我尝试创建一个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}的case对象转换为IEntityWithKey。

    我刚看到这些在线附加和分离的例子并试过它们,我对这里的Attach / Detach方法的任何新实现持开放态度。

    谢谢。

    跟进问题 -

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

    Works,但是所有加载的导航属性都是NULL,我们如何使它保持导航属性,并且当我们从上下文中分离时,不用NULL替换(或丢失)它们。

2 个答案:

答案 0 :(得分:29)

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

答案 1 :(得分:10)

到你的后续问题:

  

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

我认为在保持其导航属性的同时,无法从上下文中分离对象图。 From MSDN

  

在独立协会中,关系信息不是   为一个独立的物体维护。

虽然此声明与独立关联有关,但并不意味着导航属性在外键关联中维护(在模型中公开外键属性的关系)。是,保持“关系信息”,因为外键属性(标量属性)将处于活动状态并在分离后包含正确的外键值。但对于参考属性,相应的导航属性仍然​​是null,或者对于导航集合,将从集合中删除引用。

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

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

  

分离仅影响传递给方法的特定对象。如果   被分离的对象在对象上下文中具有相关对象   对象没有分离。

我可以想象这是DbContext API没有明确Detach方法的主要原因(与ObjectContext相比) - 分离被视为高级功能这并不像人们期望的那样。

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

  

在Entity Framework应用程序中,您可以从中分离对象   对象上下文。您可以分离对象以节省资源,如   在同一个对象上下文中执行重复查询会增加   对象上下文的内存需求。

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