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

MoX*_*lod 31 c# asp.net entity-framework entity-framework-4.1 asp.net-mvc-3

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

要求是 -

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

这是我尝试创建一个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);
            }
        }
    }
Run Code Online (Sandbox Code Playgroud)

当我在缓存中放置一个实体时,它是DynamicProxy类型而不是真正的类.

附加根本不起作用 - 我得到一个异常,我不能将类型为Dynamic_ {blahblah}的case对象转换为IEntityWithKey.

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

谢谢.

跟进问题 -

context.Entry(entity).State = EntityState.Detached;
Run Code Online (Sandbox Code Playgroud)

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

Lad*_*nka 31

IEntityWithKey是其他类型实体的接口.它适用于"大"实体.例如,EntityObject实现此接口.这些实体不被视为POCO,也不受DbContextAPI 支持.

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

正确附加DbContextAPI应该是:

dbContext.Set(typeof(entity)).Attach(entity); 
Run Code Online (Sandbox Code Playgroud)

这应该也有效:

dbContext.Entry(entity).State = EntityState.Unchanged;
Run Code Online (Sandbox Code Playgroud)

使用DbContextAPI 正确分离应该是:

dbContext.Entry(entity).State = EntityState.Detached;
Run Code Online (Sandbox Code Playgroud)

另外,对你来说,通用方法更好object.

  • 这些工作 - 但是当我分离时,我的导航属性为null,是否有一种方法可以在没有它们变为空的情况下分离,因为我在推送到缓存之前加载它们; var trackedEntity = entity as IEntityWithChangeTracker; trackedEntity.SetChangeTracker(NULL); 不做的伎俩 (3认同)
  • 我认为`dbContext.Set(typeof(entity)).Attach(entity);`应该是`dbContext.Set(entity.GetType()).Attach(entity);`不是吗? (3认同)

Sla*_*uma 10

至于你的后续问题:

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

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

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

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

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

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

分离仅影响传递给方法的特定对象.如果正在分离的对象在对象上下文中具有相关对象,则不会分离这些对象.

我可以想象这是DbContextAPI没有明确Detach方法的主要原因(与此相反ObjectContext) - 分离被认为是一种高级功能,其行为与人们的预期不同.

MSDN提到将对象从上下文"保存资源"中分离的唯一原因(再次上面的文章):

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

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