C# 事务处理范围导致阻塞?

C# 事务处理范围导致阻塞?,c#,transactions,transactionscope,C#,Transactions,Transactionscope,我正在针对数据库编写一些单元测试,我们使用事务来确保测试数据在最后被删除 我遇到了一个问题,我正在测试的方法正在使用它们自己的TransactionScope对象,并且在访问数据库时它似乎被阻塞了 这在我的测试基类中: BaseScope = new CommittableTransaction(new TransactionOptions() { IsolationLevel = IsolationLevel.ReadUnCommitted, Timeout = new System.Time

我正在针对数据库编写一些单元测试,我们使用事务来确保测试数据在最后被删除

我遇到了一个问题,我正在测试的方法正在使用它们自己的TransactionScope对象,并且在访问数据库时它似乎被阻塞了

这在我的测试基类中:

BaseScope = new CommittableTransaction(new TransactionOptions() { IsolationLevel = IsolationLevel.ReadUnCommitted, Timeout = new System.TimeSpan(0, 5, 0) });
然后在我正在测试的方法中,它会:

using (TransactionScope scope = new TransactionScope())

第二个作用域中的代码第一次接触数据库时,它将挂起。我有办法解决这个问题吗?

如果您使用的是数据库,那么您就没有进行单元测试,而您遇到的问题正是真正的单元测试使用模拟和存根的原因之一

现在,您正在进行的测试非常有价值,在某些情况下,我实际上会进行测试,而不是单元测试。我给这个早期集成测试(EIT)贴上了标签。这里的关键点是,当我们使用真实的东西而不是单元测试模拟时,我们发现了一类全新的bug。这里的关键是真实的东西。一旦你用人工事务作用域等来伪装环境,你就失去了EIT的许多好处,因为你没有捕捉到细微的交互错误,或者(在你的例子中)引入人工问题


我会找到一种方法,用足够的测试数据快速填充数据库,并在测试之外将其恢复到该状态。“重置为已知状态”脚本对此类测试非常有帮助。

当嵌套
TransactionScope
实例时,您可以最终得到分布式事务,而不是简单的本地事务。
此行为在所使用的数据库之间有所不同。例如,SQLServer2008不会升级为DTX,除非实际涉及多个数据库。另一方面,Oracle将始终升级为分布式事务,因为它不支持为单个本地事务共享连接

根据您使用的数据库和TransactionScopeOption的不同,最终可能会出现死锁。这是因为DTX通常需要表锁以确保它们可以原子地提交。例如,在Oracle中,如果启动DTX并在完成之前崩溃或丢失连接,则最终可能会出现“可疑的分布式事务”。此“有疑问”事务可能会锁定一个或多个表,阻止其他会话修改它们,直到DBA对挂起的事务ID执行
回滚强制
命令。某些数据库(如SQLServer)尝试检测此类死锁并终止一个有问题的事务。。。但这肯定会发生

我建议您从两个选项中选择一个:

  • 决定是否确实需要编写命中数据库的测试。通常情况下,您可以使用模拟或存根来避免编写更改然后回滚数据库的测试。避免这些问题是有意义的,因为它既加快了测试的速度,又消除了测试的潜在依赖性。然而,有时候你做不到这一点
  • <>强>如果你真的需要在数据库上测试你的逻辑,考虑修改你的代码,以便所有的方法使用相同的数据库连接来执行它们的SQL。这将消除分布式事务的创建,并有望解决您的问题
    您可能还想查看数据库的挂起事务视图(在Oracle中称为…在SQLServer中有此函数)。

    您必须提交基本事务才能取消阻止您的测试方法,我认为这不是您所希望的排序行为。您需要让测试方法的事务加入在基类中创建的外部“环境”(伞/父/基/外部)事务,而不是尝试创建自己的事务

    来自MSDN备注(强调我的):

    建议您创建 使用 TransactionScope类,以便 环境事务上下文是 自动为您管理<斯特朗>你 还应使用TransactionScope 和的DependentTransaction类 需要使用 跨多个服务器执行相同的事务 函数调用或多线程 致电。了解更多信息 模型,请参见主题

    创建CommitteableTransaction不需要 不自动设置环境温度 事务,即事务 您的代码在中执行。你可以得到或 通过调用 变压器的静态电流特性 全局事务对象。更多 环境交易信息, 请参阅“管理事务流” 使用TransactionScopeOption”部分 实现隐式 使用事务范围的事务 话题。如果环境事务是 未设置,资源上的任何操作 经理不是其中的一部分 交易你需要明确地 设置并重置环境事务, 确保资源管理器 在正确的交易条件下运作 上下文

    在提交CommittableTransaction之前,该事务涉及的所有资源都将被锁定。


    正如djna指出的,使用事务回滚测试期间所做的更改是相当滥用的。您的测试应该是一个好公民,在finally子句中撤消它对数据库所做的更改,以便在它之后运行的其他测试永远不会受到影响。如果您已经有很多测试表现不好,那么您现在可能不会走这条路。在这种情况下,将基础更改为使用范围设置为
    RequiresNew
    的隐式事务,并在测试方法中使用
    Required

    ,我认为这是我最终要使用的选项。我写了一些代码来创建一个新的/空的数据库,它在测试开始时运行,在测试结束时数据库被删除