这个问题已经以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));
图表中的所有关系似乎都未设置。
我该如何解决这个问题?
答案 0 :(得分:11)
@Danny Varod是对的。您应该在整个工作流程中使用一个ObjectContext
。此外,因为您的工作流程似乎是包含多个窗口的一个逻辑功能,所以它应该也可以使用单个演示者。然后,您将遵循建议的方法:每个演示者的单个上下文。你可以多次调用SaveChanges
,这样就不会破坏你的逻辑。
该问题的根源是众所周知的问题,即在POCO实体之上生成的动态代理的缺陷与POCO T4模板生成的Fixup方法相结合。当您处置它时,这些代理仍然引用上下文。因此,他们认为他们仍然依附于上下文,他们不能被附加到另一个上下文。强制它们释放对上下文的引用的唯一方法是手动分离。同时,一旦您从上下文中分离实体,它就会从相关的附加实体中删除,因为您不能在同一个图中混合使用附加实体和分离实体。
您调用的代码中实际上没有出现此问题:
itemFromA.RelatedProperty = itemFromB;
但是在Fixup方法触发的反向操作中:
itemFromB.RelatedAs.Add(itemFromA);
我认为解决这个问题的方法是:
要关闭代理创建和延迟加载,请使用:
var context = new MyContext();
context.ContextOptions.ProxyCreationEnabled = false;
你实际上可以尝试编写自定义方法来分离整个对象图,但正如你所说的那样,它被问了500次而我还没有看到工作解决方案 - 除了对新对象图的序列化和反序列化。
答案 1 :(得分:5)
我认为你有几个不同的选择,其中2个是:
在您完成该过程之前保持上下文有效,仅使用1个上下文,而不是2个。
一个。在处理上下文#1之前,使用BinaryStreamer或ValueInjecter或AutoMapper等工具创建图的深层克隆。
湾合并从上下文#2到克隆图的更改。
℃。保存后,将克隆图形中的更改合并到由新ObjectContext创建的图形中。
为了将来参考,此MSDN博客链接可以帮助您决定在以下情况下做什么: http://blogs.msdn.com/b/dsimmons/archive/2008/02/17/context-lifetimes-dispose-or-reuse.aspx
答案 2 :(得分:3)
我认为您不需要分离来解决问题。
我们这样做:
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的输入,更新更清晰。
答案 3 :(得分:0)
好的,我知道您的对象上下文早已消失。
但是让我们以这种方式看待它,Entity Framework实现了工作单元概念,它跟踪您在对象图中所做的更改,以便它可以生成与您所做的更改相对应的SQL。如果不依赖于上下文,就无法应对变化。
如果您无法控制上下文,那么我认为您无法做任何事情。
否则有两个选项,
但即使是在自我跟踪的情况下,您仍然可能会遇到一些问题。