TransactionScope过早完成
我有一个在TransactionScope中运行的代码块,在这个代码块中,我对数据库进行了几次调用。 select,更新,创build和删除整个色域。 当我执行我的删除我执行它使用SqlCommand的扩展方法,将自动重新提交查询,如果它死锁,因为这个查询可能会遇到死锁。
我相信当遇到死锁,并且该函数试图重新提交查询时,会发生此问题。 这是我收到的错误:
与当前连接相关的交易已经完成但尚未处理。 事务必须在连接可以用来执行SQL语句之前进行处理。
这是执行查询的简单代码(下面的所有代码都是在使用TransactionScope的情况下执行的):
using (sqlCommand.Connection = new SqlConnection(ConnectionStrings.App)) { sqlCommand.Connection.Open(); sqlCommand.ExecuteNonQueryWithDeadlockHandling(); }
以下是重新提交死锁查询的扩展方法:
public static class SqlCommandExtender { private const int DEADLOCK_ERROR = 1205; private const int MAXIMUM_DEADLOCK_RETRIES = 5; private const int SLEEP_INCREMENT = 100; public static void ExecuteNonQueryWithDeadlockHandling(this SqlCommand sqlCommand) { int count = 0; SqlException deadlockException = null; do { if (count > 0) Thread.Sleep(count * SLEEP_INCREMENT); deadlockException = ExecuteNonQuery(sqlCommand); count++; } while (deadlockException != null && count < MAXIMUM_DEADLOCK_RETRIES); if (deadlockException != null) throw deadlockException; } private static SqlException ExecuteNonQuery(SqlCommand sqlCommand) { try { sqlCommand.ExecuteNonQuery(); } catch (SqlException exception) { if (exception.Number == DEADLOCK_ERROR) return exception; throw; } return null; } }
错误发生在线上:
sqlCommand.ExecuteNonQuery();
不要忘记从你的TransactionScope中压缩你的select语句。 在SQL Server 2005及更高版本中,即使在使用(nolock)的情况下,仍会在选定的表上创build锁。 检查了这一点,它显示了如何设置和使用TransactionScope 。
using(TransactionScope ts = new TransactionScope { // db calls here are in the transaction using(TransactionScope tsSuppressed = new TransactionScope (TransactionScopeOption.Suppress)) { // all db calls here are now not in the transaction } }
我发现,当事务运行的时间超过了System.Transactions
的maxTimeout
时,就会出现这个消息。 TransactionOptions.Timeout
增加并不重要,它不能超过maxTimeout
。
maxTimeout
的默认值设置为10分钟,其值只能在machine.config
修改
将以下(在configuration级别中)添加到machine.config
以修改超时值:
<configuration> <system.transactions> <machineSettings maxTimeout="00:30:00" /> </system.transactions> </configuration>
可以在以下位置findmachine.config: %windir%\Microsoft.NET\Framework\[version]\config\machine.config
你可以阅读更多关于它在这篇博客文章: http : //thecodesaysitall.blogspot.se/2012/04/long-running-systemtransactions.html
我可以重现这个问题。 这是一个事务超时。
using (new TransactionScope(TransactionScopeOption.Required, new TimeSpan(0, 0, 0, 1))) { using (SqlConnection connection = new SqlConnection(connectionString)) { connection.Open(); using (var sqlCommand = connection.CreateCommand()) { for (int i = 0; i < 10000; i++) { sqlCommand.CommandText = "select * from actor"; using (var sqlDataReader = sqlCommand.ExecuteReader()) { while (sqlDataReader.Read()) { } } } } } }
抛出System.InvalidOperationExceptionexception与此消息:“与当前连接关联的事务已完成但尚未处理,事务必须在连接可用于执行SQL语句之前处置。
为了解决这个问题,可以使查询运行得更快或者延长超时。
如果一个exception发生在一个TransactionScope
它将被回滚。 这意味着TransactionScope
已经完成。 你现在必须调用dispose()
并开始一个新的事务。 老实说,我不确定是否可以重新使用旧的TransactionScope
,我从来没有尝试过,但我不认为。
我的问题是一个愚蠢的,如果你坐在debugging突破超时,你会得到这个。 面对棕榈
男人,编程让你感觉有些日子…
确认此错误也可能是由事务超时造成的。 只要添加到Marcus + Rolf所说的话,如果你没有明确地在TransactionScope
上设置超时,那么超时TimeSpan将会默认一个默认值。 这个默认值是以下值中的较小值:
-
如果你已经覆盖了本地的
app.config
/web.config
设置,例如<system.transactions> <defaultSettings timeout="00:05:00" /> </system.transactions>
-
但是,这是在
machine.config
设置<machineSettings maxTimeout="00:10:00" />
禁用 Microsoft Distributed Transaction Coordinator
器也可能导致此exception。
如果要启用它,我们运行“ dcomcnfg ”并select"Component Services" -> "My Computer" -> "Distributed Transaction Coordinator" -> "Local Service DTC"
并select“ 属性 ”。
应该勾选“ 允许远程客户端 ”,“ 允许入站 ”,“ 允许出站 ”和“ 不需要validation ”。