实体框架多对象上下文

本文关键字:上下文 多对象 框架 实体 | 更新日期: 2023-09-27 17:50:28

这个问题已经被用50种不同的方式问了500次…但又来了,因为我似乎找不到我想要的答案:

我正在使用带有POCO代理的EF4。

。我有一个从ObjectContext的一个实例中获取的对象图。ObjectContext被处理了

B。我从ObjectContext的另一个实例中获取了一个对象。那个ObjectContext也被处置了。

我想设置一个相关的属性从a使用实体在B....的一堆东西就像

foreach(var itemFromA in collectionFromA)
{
   itemFromA.RelatedProperty = itemFromB;
}

当我这样做的时候,我得到了异常:

System.InvalidOperationException occurred
  Message=The relationship between the two objects cannot be defined because they are attached to different ObjectContext objects.
  Source=System.Data.Entity
  StackTrace:
       at System.Data.Objects.DataClasses.RelatedEnd.Add(IEntityWrapper wrappedTarget, Boolean applyConstraints, Boolean addRelationshipAsUnchanged, Boolean relationshipAlreadyExists, Boolean allowModifyingOtherEndOfRelationship, Boolean forceForeignKeyChanges)
       at System.Data.Objects.DataClasses.RelatedEnd.Add(IEntityWrapper wrappedEntity, Boolean applyConstraints)
       at System.Data.Objects.DataClasses.EntityReference`1.set_ReferenceValue(IEntityWrapper value)
       at System.Data.Objects.DataClasses.EntityReference`1.set_Value(TEntity value)
       at 

我想我需要从ObjectContexts中分离这些实体,当他们处置为了上面的工作…问题是,当我的ObjectContext处理时,从它分离所有实体似乎会破坏图。如果我这样做:

objectContext.ObjectStateManager.GetObjectStateEntries(EntityState.Added | EntityState.Deleted | EntityState.Modified | EntityState.Unchanged)  
.Select(i => i.Entity).OfType<IEntityWithChangeTracker>().ToList()  
.ForEach(i => objectContext.Detach(i));

图中所有的关系似乎都没有设置。

我该如何着手解决这个问题呢?

实体框架多对象上下文

@Danny Varod是对的。您应该为整个工作流程使用一个ObjectContext。此外,因为你的工作流看起来像一个包含多个窗口的逻辑特性,它可能也应该使用单个演示者。然后您将遵循推荐的方法:每个演示者一个上下文。你可以多次调用SaveChanges,这样它就不会破坏你的逻辑。

这个问题的根源是众所周知的缺乏在POCO实体之上生成的动态代理和POCO T4模板生成的Fixup方法的问题。当您处置上下文时,这些代理仍然持有对上下文的引用。正因为如此,他们认为他们仍然依附于这个环境,他们不能依附于另一个环境。强制它们释放对上下文的引用的唯一方法是手动分离。同时,一旦你将一个实体从上下文中分离出来,它就会从相关的附加实体中移除,因为你不能在同一个图中同时拥有附加实体和分离实体。

这个问题实际上不会发生在你调用的代码中:

itemFromA.RelatedProperty = itemFromB;

但在Fixup方法触发的反向操作中:

itemFromB.RelatedAs.Add(itemFromA);

我认为解决这个问题的方法是:

  • 不要这样做,在整个工作单元中使用单个上下文——这是假定的用法。
  • 删除反向导航属性,以便Fixup方法不会触发该代码。
  • 不要使用带有Fixup方法的POCO T4模板或修改T4模板以不生成它们。
  • 关闭这些操作的延迟加载和代理创建。这将从poco中删除动态代理,因此它们将独立于上下文。

关闭代理创建和延迟加载,使用:

var context = new MyContext();
context.ContextOptions.ProxyCreationEnabled = false;

你实际上可以尝试编写自定义方法来分离整个对象图,但正如你所说,它被要求了500次,我还没有看到有效的解决方案-除了对新对象图的序列化和反序列化。

我想你这里有几个不同的选择,其中两个是:

  1. 在进程完成之前保持上下文活动,只使用1个上下文,而不是2个。

  2. 。在处理上下文#1之前,使用BinaryStreamer或valueinjector或AutoMapper等工具创建一个图形的深度克隆。

    b。将上下文#2中的更改合并到克隆图中。

    c。保存后,将克隆图中的更改合并到由新ObjectContext创建的图中。


为了将来的参考,这个MSDN博客链接可以帮助你决定什么时候做:http://blogs.msdn.com/b/dsimmons/archive/2008/02/17/context-lifetimes-dispose-or-reuse.aspx

我认为你不需要分离来解决问题。

我们这样做:

public IList<Contact> GetContacts()
{
  using(myContext mc = new mc())
  {
    return mc.Contacts.Where(c => c.City = "New York").ToList();
  }
}
public IList<Sale> GetSales()
{ 
  using(myContext mc = new mc())
  {
    return mc.Sales.Where(c => c.City = "New York").ToList();
  }  
}
public void SaveContact(Contact contact)
{
    using (myContext mc = new myContext())
    {
       mc.Attach(contact);
       contact.State = EntityState.Modified;
       mc.SaveChanges();
    }
}
public void Link()
{
   var contacts = GetContacts();
   var sales = GetSales();
   foreach(var c in contacts)
   {
       c.AddSales(sales.Where(s => s.Seller == c.Name));
       SaveContact(c);
   }
}

这允许我们提取数据,将其传递给另一层,让他们做任何他们需要做的事情,然后传递回来,我们更新或删除它。我们使用单独的上下文(每个方法一个)(每个请求一个)来完成所有这些。

重要的是要记住,如果你使用IEnumerables,它们是延迟执行的。这意味着它们实际上不会提取信息,直到你对它们进行计数或迭代。所以如果你想在你的上下文中使用它,你必须做一个ToList(),这样它就会被迭代并创建一个列表。然后你就可以使用这个列表了。

编辑更新更清楚,感谢@Nick的输入

好的,我知道你的对象上下文早就消失了。

但是让我们这样看,实体框架实现了工作单元的概念,它跟踪你在对象图中所做的更改,因此它可以生成与你所做的更改相对应的SQL。如果不依附于上下文,它就无法进行更改。

如果你不能控制上下文,那么我认为你什么都做不了。

否则有两个选项,

  1. 保持你的对象上下文存活更长的生命周期,如用户登录的会话等
  2. 尝试使用自跟踪文本模板重新生成代理类,该模板将启用断开连接状态下的更改跟踪。

但是即使在自我跟踪的情况下,你仍然可能会遇到一些小问题。