【问题标题】:dbUpdateConcurrencyException on simple delete after loading加载后简单删除时出现 dbUpdateConcurrencyException
【发布时间】:2015-11-13 16:52:51
【问题描述】:

我已经阅读了一些关于可能导致dbUpdateConcurrencyException 的问题的问题和文章,但它们似乎都与我的代码中发生的事情无关。我对使用 Entity Framework 6.0 的 CRUD 操作进行了简单的功能测试。

我的[TestInitialize] 插入一些数据,我的[TestCleanup] 删除相同的数据。这适用于我的所有测试,除了删除记录的测试。当调用该测试的清理时,它会抛出一个dbUpdateConcurrencyException

我的测试清理方法是这样的:

    var contracts = this.Context.Contract
        .Where(c => c.EntryUserID == "FunctionalTests.TestData").ToList();
    this.Context.Contract.RemoveRange(contracts);

    this.Context.SaveChanges();

    var customers = this.Context.Customer
        .Where(c => c.EntryUserID == "FunctionalTests.TestData").ToList();
    this.Context.Customer.RemoveRange(customers);

    this.Context.SaveChanges(); //This throws dbUpdateConcurrencyException??

在此失败之前运行的测试会从Context.Contract 中删除一条记录。一些奇怪的事情:

  • Context.Contract 的清理工作正常。尽管 Customer.Contract 是在测试期间被删除的内容,但对 Context.Customer 的清理是破坏性的。
  • 我正在加载要删除的实体,然后再尝试删除它们。它们不可能在加载和尝试删除之间被修改。我的理解是,当实体在加载和尝试删除之间被修改时会发生此异常。

Contract 确实有一个指向 CustomerId 的外键。我猜这是相关的;删除合同将更改 Customer 实体,因为 Customer 实体具有合同属性的集合。但同样,我在删除合同后重新加载客户;所以它们不会在加载和删除之间被修改。

我尝试过一次删除一项,而不是使用RemoveRange();第一次删除时会发生同样的错误。在尝试删除 Customer 之前,我尝试在特定的 Customer 实体上调用 Reload();同样的结果。

另请注意,这适用于所有其他不删除合同的测试。因此,在删除客户之前删除所有合同可以正常工作。只有在删除了清理中的其余合同之前删除了单个合同。

这是完整的错误消息/堆栈跟踪:

System.Data.Entity.Infrastructure.DbUpdateConcurrencyException:System.Data.Entity.Infrastructure.DbUpdateConcurrencyException:存储更新、插入或删除语句影响了意外的行数 (0)。自加载实体后,实体可能已被修改或删除。有关理解和处理乐观并发异常的信息,请参阅http://go.microsoft.com/fwlink/?LinkId=472540。 ---> System.Data.Entity.Core.OptimisticConcurrencyException:存储更新、插入或删除语句影响了意外的行数 (0)。自加载实体后,实体可能已被修改或删除。有关理解和处理乐观并发异常的信息,请参阅http://go.microsoft.com/fwlink/?LinkId=472540。 结果堆栈跟踪:
在 System.Data.Entity.Internal.InternalContext.SaveChanges() 在 System.Data.Entity.Internal.LazyInternalContext.SaveChanges() 在 System.Data.Entity.DbContext.SaveChanges()

内部异常具有完全相同的类型和消息,具有此堆栈跟踪:

在 System.Data.Entity.Core.Mapping.Update.Internal.UpdateTranslator.ValidateRowsAffected(Int64 rowsAffected,UpdateCommand 源) 在 System.Data.Entity.Core.Mapping.Update.Internal.UpdateTranslator.Update() 在 System.Data.Entity.Core.EntityClient.Internal.EntityAdapter.b__2(UpdateTranslator ut) 在 System.Data.Entity.Core.EntityClient.Internal.EntityAdapter.Update[T](T noChangesResult, Func2 updateFunction) at System.Data.Entity.Core.EntityClient.Internal.EntityAdapter.Update() at System.Data.Entity.Core.Objects.ObjectContext.<SaveChangesToStore>b__35() at System.Data.Entity.Core.Objects.ObjectContext.ExecuteInTransaction[T](Func1 func, IDbExecutionStrategy executionStrategy, Boolean startLocalTransaction, Boolean releaseConnectionOnSuccess) 在 System.Data.Entity.Core.Objects.ObjectContext.SaveChangesToStore(SaveOptions 选项,IDbExecutionStrategy executionStrategy,布尔 startLocalTransaction) 在 System.Data.Entity.Core.Objects.ObjectContext.c__DisplayClass2a.b__27() 在 System.Data.Entity.SqlServer.DefaultSqlExecutionStrategy.Execute[TResult](Func`1 操作) 在 System.Data.Entity.Core.Objects.ObjectContext.SaveChangesInternal(SaveOptions 选项,布尔型 executeInExistingTransaction) 在 System.Data.Entity.Core.Objects.ObjectContext.SaveChanges(SaveOptions 选项) 在 System.Data.Entity.Internal.InternalContext.SaveChanges()

更新

通过将我在数据设置/拆卸中使用的DbContext 传递给正在测试的方法,我能够让它工作/解决它。换句话说,错误似乎是因为有一个DbContext 用于测试设置/拆卸,而另一个DbContext 用于正在测试的代码。

但是,这并不能真正回答我的任何问题。我宁愿不必传递我的DbContext 以确保它是相同的;有没有办法告诉我的上下文从实际数据库中刷新/加载,而不仅仅是使用它知道的内容?而且,这根本无法解释为什么删除合同会正常工作,并且只会在删除客户时失败。

【问题讨论】:

  • 为什么在一个地方使用两次 SaveChanges() 调用?我知道这不相关。顺便说一句,您是否检查了两个 DbContext 实例的哈希码?
  • @CodeNotFound 我插入了另一个 SaveChanges() 以确认该问题与在删除合同之前尝试删除客户无关;最初它只有一个 SaveChanges()。
  • 您能告诉我们dbUpdateConcurrencyException 中的确切消息吗?我知道这是一个并发异常,但有时您可以在 InnerException 消息中包含很多信息。
  • @CodeNotFound,我用完整的错误和堆栈跟踪更新了问题。
  • @GendoIkari 你能通过将DbContext.Configuration.ProxyCreationEnabled 设置为false 来关闭代理创建并报告结果吗?

标签: c# .net entity-framework concurrency entity-framework-6


【解决方案1】:

此错误是因为测试设置运行时正在加载测试设置和拆卸使用的 DBContext。不同的上下文会在设置和拆卸之间更改数据库,并且因为第一个上下文是在设置期间加载的,所以它基于数据库的“旧”版本,即运行测试之前的版本。

解决方案是不让拆卸使用与设置使用的 DBContext 相同的 DBContext。 Teardown 应该加载自己的新 DBContext,以便根据最新数据加载。

我假设重要的是从数据库加载要删除的客户时的数据状态。但显然重要的是加载 DBContext 时的数据状态。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-01-24
    • 2018-01-27
    • 2013-10-18
    • 1970-01-01
    • 2019-01-22
    • 1970-01-01
    • 2019-04-02
    相关资源
    最近更新 更多