如何防止 Entity Framework 4.x 中出现孤立对象?
How do I prevent orphaned objects in Entity Framework 4.x?
我有以下(伪)代码...实际代码要复杂得多,但这是根本问题的要点:
string customXML = GoFetchCustomXML();
using (MyContext ctx = new MyContext(...))
{
SomeTable tbl = CreateEntryInTable(customXML);
ctx.SomeTables.AddObject(tbl);
ctx.SaveChanges();
}
...
public SomeTable CreateEntryInTable(string customXML)
{
XDocument doc = XDocument.Parse(customXML);
SomeTable ret = new SomeTable();
foreach (XElement descendant in doc.Descendants("ChildObject").ToList())
{
ChildTable ct = new ChildTable();
// Set some initial items about ct based on
// customer configurations. It sets our StatusCodeID to "NEW".
initializeCT(ct, SomeGlobalCustomerObject);
if (ValidateChildObject(descendant, ct))
{
// Set final ct properties here. We move the
// StatusCodeID to "Valid" among many other things.
// Before we go on, set CreateDate
ct.CreateDate = DateTime.Now;
ret.ChildTables.AddObject(ct);
} else {
// Do nothing. We've changed our mind about needing
// a ChildTable object.
}
}
return ret;
}
我今天花了 8 个多小时来解决这个非常奇怪的问题。我遇到了一个神秘错误:The element at index 0 in the collection of objects to refresh is in the added state. Objects in this state cannot be refreshed.
当我 运行 代码时,在 "descendant" 的第三个循环中它没有通过验证,所以它永远不会添加到 ChildTables。这应该是有效的,对吧? (如果没有,请告诉我!)
然而——正如我所深切发现的——它已经以某种方式被添加到上下文中。它的标识列实际上是“0”。当程序到达 "SaveChanges()" 时,它崩溃了,因为记录上的日期(创建日期)是 00/00/0001,这在数据库中是无效的。当我将探查器放在连接上时,我看到它的 StatusCodeID == NEW... 但此记录从未完成,也从未添加到 CTX 对象或 ChildTables。
更糟糕的是,既然处于这种状态,上下文就完蛋了。我找不到要杀死它的记录,而且我无法保存任何内容,因为它是上下文中某处的孤立记录。
如果我跳过 "invalid" 对象或重写我的代码,以便在我们确定真正需要它并将其添加到 ChildTables 之前不创建该对象,那么它就可以工作。但是我上面所做的应该是合法的,不是吗?如果不是,有人可以解释为什么吗?
我找到了答案,或者至少找到了一个非常重要的解决方法。
1) 不要创建对象,除非我相当确定我将需要添加它们。我可以修改 ValidateChildObject 以获取 Descendant 和 SomeCustomerObject 以确定它是否有效,并且仅在规则通过时创建和初始化 CT。
2) 但是,在某些情况下,这在当前设计中是不可行的,因为它会减慢速度——无论我必须做什么来验证某些设置,我都必须再做一次才能设置initializeCT 中的那些值。在那些情况下,如上面的 "ELSE" 子句,我需要做:
...
}
else
{
// Remove it from the ChildTables anyway, just in case
// it was magically added. If it was not added, this does not fail.
ctx.ChildTables.DeleteObject(ct);
}
当我采用这两种方法之一时,我的代码运行流畅。
我有以下(伪)代码...实际代码要复杂得多,但这是根本问题的要点:
string customXML = GoFetchCustomXML();
using (MyContext ctx = new MyContext(...))
{
SomeTable tbl = CreateEntryInTable(customXML);
ctx.SomeTables.AddObject(tbl);
ctx.SaveChanges();
}
...
public SomeTable CreateEntryInTable(string customXML)
{
XDocument doc = XDocument.Parse(customXML);
SomeTable ret = new SomeTable();
foreach (XElement descendant in doc.Descendants("ChildObject").ToList())
{
ChildTable ct = new ChildTable();
// Set some initial items about ct based on
// customer configurations. It sets our StatusCodeID to "NEW".
initializeCT(ct, SomeGlobalCustomerObject);
if (ValidateChildObject(descendant, ct))
{
// Set final ct properties here. We move the
// StatusCodeID to "Valid" among many other things.
// Before we go on, set CreateDate
ct.CreateDate = DateTime.Now;
ret.ChildTables.AddObject(ct);
} else {
// Do nothing. We've changed our mind about needing
// a ChildTable object.
}
}
return ret;
}
我今天花了 8 个多小时来解决这个非常奇怪的问题。我遇到了一个神秘错误:The element at index 0 in the collection of objects to refresh is in the added state. Objects in this state cannot be refreshed.
当我 运行 代码时,在 "descendant" 的第三个循环中它没有通过验证,所以它永远不会添加到 ChildTables。这应该是有效的,对吧? (如果没有,请告诉我!)
然而——正如我所深切发现的——它已经以某种方式被添加到上下文中。它的标识列实际上是“0”。当程序到达 "SaveChanges()" 时,它崩溃了,因为记录上的日期(创建日期)是 00/00/0001,这在数据库中是无效的。当我将探查器放在连接上时,我看到它的 StatusCodeID == NEW... 但此记录从未完成,也从未添加到 CTX 对象或 ChildTables。
更糟糕的是,既然处于这种状态,上下文就完蛋了。我找不到要杀死它的记录,而且我无法保存任何内容,因为它是上下文中某处的孤立记录。
如果我跳过 "invalid" 对象或重写我的代码,以便在我们确定真正需要它并将其添加到 ChildTables 之前不创建该对象,那么它就可以工作。但是我上面所做的应该是合法的,不是吗?如果不是,有人可以解释为什么吗?
我找到了答案,或者至少找到了一个非常重要的解决方法。
1) 不要创建对象,除非我相当确定我将需要添加它们。我可以修改 ValidateChildObject 以获取 Descendant 和 SomeCustomerObject 以确定它是否有效,并且仅在规则通过时创建和初始化 CT。
2) 但是,在某些情况下,这在当前设计中是不可行的,因为它会减慢速度——无论我必须做什么来验证某些设置,我都必须再做一次才能设置initializeCT 中的那些值。在那些情况下,如上面的 "ELSE" 子句,我需要做:
...
}
else
{
// Remove it from the ChildTables anyway, just in case
// it was magically added. If it was not added, this does not fail.
ctx.ChildTables.DeleteObject(ct);
}
当我采用这两种方法之一时,我的代码运行流畅。