我从Entity Framework 4.3.1获得此异常(顺便提一下,从Visual Studio 2010中针对.NET 4构建的ASP .NET MVC 3 Web应用程序):
System.InvalidOperationException:已成功提交对数据库的更改,但更新对象上下文时发生错误。 ObjectContext可能处于不一致状态。内部异常消息:无法将对象添加到对象上下文。对象的EntityKey有一个ObjectStateEntry,表示该对象已经参与了不同的关系。
我做了一些谷歌搜索,我来到这里(在msdn上):
InvalidOperationException when saving
Jeff Derstadt(接受的答案)说:
当您调用SaveChanges时,上下文会保留更改,然后尝试修复状态条目。当它执行此操作时,它执行“密钥修正”,其中将临时EntityKeys转换为完整的EntityKeys。在这种情况下,临时密钥TKP2现在将等于P2的密钥。上下文尝试将状态管理器中的密钥条目“提升”为完整的实体条目,并在此过程中修复图形。这是因为存在冲突而发生异常的地方:P1.Spouse = P2,因此P2.Spouse应该= P1,但是因为添加了项目,所以P2.Spouse已经等于P3而且EntityReference不能有多个值。不幸的是,在数据库保存发生之前并不总是可以检测到这种情况,因为临时EntityKeys不等同于完整的EntityKeys,所以我们无法识别这样的未来类别。
我在想这里有点“脏”的方式,所以我的问题就是这个......
因为在保存更改之后我不太关心对象图形是什么样的(这是我的请求的结束因此被丢弃了)并且数据已被插入(当我查看SQL时实际上是完全正常的)我让它在调试器中继续运行后的服务器) - 有没有一种方法我可以告诉实体框架不执行此步骤(因此不会得到此异常,并且可能会节省一点时间)?
答案 0 :(得分:1)
如果你想要更好的帮助,你应该发布一些代码。
尽管如此,我还是发现自己正在解决其中的一些错误,并提出了一个基本的事实:如果你引用了数据库中已经存在的东西,请参考它。不要只使用键或代码,使用DbContexts中表示的实际对象。您可以使用DbContext.Entry()方法找到它们。
另一方面,如果原始和引用的对象都是在运行时创建的,请确保它们被赋予唯一的ID,并且该ID在运行时由实体框架(而不是数据库)知道。否则(例如,如果您依赖于NEWID()Sql函数),对象A可能引用对象B,其运行时ID不等于同一对象在数据库中存储后的ID。
正如我所说,如果您发布一些代码,我们可能会提供更好的帮助;)
答案 1 :(得分:1)
没有正式的方法可以做到这一点,但试试这个:
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();
}
}