是否可以在保存更改后停止实体框架"fixing up"关系?



我从实体框架4.3.1中得到了这个异常(顺便提一下,来自Visual Studio 2010中针对.NET 4构建的ASP.NET MVC 3 web应用程序):

System.InvalidOperationException:已成功提交对数据库的更改,但在更新对象上下文时出错。ObjectContext可能处于不一致的状态。内部异常消息:无法将对象添加到对象上下文中。该对象的EntityKey具有一个ObjectStateEntry,该ObjectStateEntry指示该对象已参与不同的关系。

我在谷歌上搜索了一下,发现了这个(在msdn上):

保存时出现InvalidOperationException

Jeff Derstadt(公认的答案)说:

当您调用SaveChanges时,上下文会保留更改,然后尝试修复状态条目。当它这样做时,它执行"密钥修复",将临时EntityKeys转换为完整EntityKeys。在这种情况下,临时密钥TKP2现在将等于P2的密钥。上下文试图将状态管理器中的键条目"提升"为完整的实体条目,并在此过程中修复图形。以下是由于存在冲突而发生异常的位置:P1。配偶=P2,因此P2。配偶应该=P1,但由于添加了项,P2。配偶已经等于P3,EntityReference不能有多个值。不幸的是,在数据库保存之前并不总是能够检测到这种情况,因为临时EntityKeys不等同于完整的EntityKeys,因此我们无法识别未来类似的冲突。

我在这里的想法有点"肮脏",所以我的问题是。。。

由于我并不真正关心保存更改后(这是我的请求的末尾,所以无论如何都会被丢弃)和数据插入后(在调试器中继续运行后,当我在SQL Server中查看它时,它实际上是完全正常的)对象图的外观,有没有办法告诉实体框架不要执行这一步骤(因此没有得到这个例外,大概也节省了一点时间)?

如果想要更好的帮助,您应该发布一点代码。

尽管如此,我还是发现自己在与其中的一些错误作斗争,并得出了一个基本事实:如果你引用的是数据库中已经存在的东西,一定要引用它。不要只使用键或代码,而是使用DbContexts中表示的实际对象。您可以使用DbContext.Entry()方法找到它们。

另一方面,如果原始对象和引用对象都是在运行时创建的,请确保为它们提供了唯一的ID,并且实体框架(而不是数据库)在运行时知道该ID。否则(例如,如果您依赖NEWID()Sql函数),则可能是对象a引用的对象B的运行时ID不等于同一对象存储在数据库中后获得的ID。

正如我所说,如果你能发布一些代码,我们可能会提供更好的帮助;)

没有官方的方法可以做到这一点,但可以尝试一下:

private static readonly Lazy<MethodInfo> internalDeleteMethod = new Lazy<MethodInfo>(
() => typeof(ObjectContext).Assembly.GetType("System.Data.Objects.EntityEntry").GetMethod("Delete", BindingFlags.Instance | BindingFlags.NonPublic, null, new[] { typeof(bool) }, null)); 

public static void Delete<TEntity>(Func<DbContext> dbContextFactory, TEntity entity,
Func<DbContext, IEnumerable<DbEntityValidationResult>> onSaving = null,
Action<DbContext> onSaved = null,
bool referenceFixup = true)
where TEntity : class
{
Guard.ArgumentNotNull(dbContextFactory, "dbContextFactory");
Guard.ArgumentNotNull(entity, "entity");
onSaving = onSaving ?? (_ => null);
onSaved = onSaved ?? (_ => { });
using (var dbContext = dbContextFactory())
{
var set = dbContext.Set<TEntity>();
set.Attach(entity);
var entry = ((IObjectContextAdapter)dbContext).ObjectContext.ObjectStateManager.GetObjectStateEntry(entity);
internalDeleteMethod.Value.Invoke(entry, new object[] { referenceFixup });
dbContext.SaveChanges();
}
}

最新更新