6

我从 Entity Framework 4.3.1 中得到了这个异常(顺便说一下,来自在 Visual Studio 2010 中针对 .NET 4 构建的 ASP .NET MVC 3 Web 应用程序):

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

我做了一些谷歌搜索,然后我来到了这个(在 msdn 上):

保存时出现 InvalidOperationException

在那个 Jeff Derstadt (接受的答案)中说:

然后,当您调用 SaveChanges 时,上下文会保留更改,然后尝试修复状态条目。当它这样做时,它会执行“密钥修复”,将临时 EntityKeys 转换为完整的 EntityKeys。在这种情况下,临时密钥 TKP2 现在将等于 P2 的密钥。上下文尝试将状态管理器中的密钥条目“提升”为完整的实体条目,并在此过程中修复图形。这是因为存在冲突而发生异常的地方:P1.Spouse = P2,因此 P2.Spouse 应该 = P1,但由于添加了项目,P2.Spouse 已经等于 P3,并且 EntityReference 不能有多个值。

我在这里以一种“肮脏”的方式思考,所以我的问题是......

因为我并不真正关心保存更改后对象图的样子(这是我的请求的结束,所以无论如何都被丢弃了)并且数据已被插入(当我在 SQL Server 中查看它时实际上完全没问题在我让它在调试器中继续运行之后) - 有没有办法告诉实体框架不要执行此步骤(因此不会出现此异常,并且可能也节省了一些时间)?

4

2 回答 2

1

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

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

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

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

于 2012-12-10T11:01:11.147 回答
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();
     }
 }
于 2013-10-16T09:19:32.290 回答