TransactionScope 过早完成
Posted
技术标签:
【中文标题】TransactionScope 过早完成【英文标题】:TransactionScope Prematurely Completed 【发布时间】:2011-02-24 16:50:00 【问题描述】:我有一个在 TransactionScope 内运行的代码块,在这个代码块中我对数据库进行了多次调用。选择、更新、创建和删除,整个范围。当我执行删除时,我使用 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();
【问题讨论】:
【参考方案1】:不要忘记从 TransactionScope 中禁止选择语句。在 SQL Server 2005 及更高版本中,即使您使用 with(nolock),仍然会在 select 触及的那些表上创建锁。看看这个,它会告诉你how to setup and use 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
【讨论】:
感谢有关在 select 语句中抑制事务的提示。这有助于解决让我抓狂的超时问题。 很棒的答案。这让我对 sql 指令的选择/插入集合感到疯狂。添加 Suppress 选项会自动解决问题。 天哪,谢谢!我整天都在为此苦苦挣扎。如此简单的解决方案。 过度锁定通常是默认Serializable
TS 隔离级别的结果。这通常最好通过将 TS 创建包装在一个 classfactory 中来解决,该 classfactory 将序列化设置为更多sane like Read Comitted
我不明白。这与错误消息有什么关系?链接的文章是关于分布式事务的。以及为什么要抑制任何内容 - 锁定多少由隔离级别决定。【参考方案2】:
我发现当事务运行的时间长于System.Transactions
的maxTimeout
时,可能会出现此消息。 TransactionOptions.Timeout
增加没关系,不能超过maxTimeout
。
maxTimeout
的默认值设置为10分钟,其值只能在machine.config
中修改
在machine.config
中添加以下内容(在配置级别)以修改超时:
<configuration>
<system.transactions>
<machineSettings maxTimeout="00:30:00" />
</system.transactions>
</configuration>
machine.config 位于:%windir%\Microsoft.NET\Framework\[version]\config\machine.config
您可以在这篇博文中了解更多信息:http://thecodesaysitall.blogspot.se/2012/04/long-running-systemtransactions.html
【讨论】:
请记住,配置文件区分大小写,因此它应该是“machineSettings”和“maxTimeout”。太糟糕了,你不能在你的 app.config 文件中覆盖它:( 另外注意你必须把它放在config部分的end,否则会报错。【参考方案3】:我可以重现该问题。这是事务超时。
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.InvalidOperationException
并发送此消息:
与当前连接相关的事务已经完成 但尚未处置。交易必须在之前处理 该连接可用于执行 SQL 语句。
为了解决这个问题,让您的查询运行得更快或增加超时时间。
【讨论】:
【参考方案4】:如果在 TransactionScope
中发生异常,它会被回滚。这意味着TransactionScope
已完成。您现在必须调用dispose()
并开始一个新事务。老实说,我不确定您是否可以重复使用旧的 TransactionScope
,我从未尝试过,但我认为不会。
【讨论】:
即使捕获到异常事务也会回滚? 我从来没有像我一样尝试过它,异常 = 错误 = 停止和回滚。但是,从您所描述的情况来看,似乎是这样。 这是错误的,不是发生在异常上的情况。您也不必调用 Dispose() 。当在 using 语句中生成 TransactionScope 时,using 语句将 Dispose() TransactionScope on Exceptions。 在 Dispose() 上,TransactionScope 将回滚或提交,具体取决于天气 TransactionScope.Complete() 是否被调用。这就是为什么在结束 using 块之前必须将 Complete() 作为最后一件事调用的原因。您当然也可以手动使用 try-finally 阻止 Dispose()。然而,这不会改变任何关于 Dispose()、异常、回滚行为的假设错误。抱歉,不得不投反对票。 我也有同样的问题,但可以理解答案。我是这样给的。使用 (TransactionScope 范围 = new TransactionScope(TransactionScopeOption.RequiresNew))【参考方案5】:我的问题是一个愚蠢的问题,如果您通过超时进行调试中断,您将得到这个。 掌心
伙计,编程让你有些日子感觉很沉重......
【讨论】:
【参考方案6】:已确认此错误也可能是由事务超时引起的。只是为了补充 Marcus + Rolf 所说的,如果您没有在 TransactionScope
上明确设置超时,超时 TimeSpan 将采用默认值。此默认值是以下的较小:
如果您已覆盖本地 app.config
/ web.config
设置,例如
<system.transactions>
<defaultSettings timeout="00:05:00" />
</system.transactions>
但这会在machine.config
设置<machineSettings maxTimeout="00:10:00" />
处“封顶”
【讨论】:
我可以在 app.config 文件中覆盖这个吗?【参考方案7】:此异常也可能由禁用 Microsoft Distributed Transaction Coordinator
引起。
如果我们想启用它,我们运行“dcomcnfg”并选择"Component Services" -> "My Computer" -> "Distributed Transaction Coordinator" -> "Local Service DTC"
并选择“Properties”。
应勾选“允许远程客户端”、“允许入站”、“允许出站”和“不验证必填”。
【讨论】:
您可以启用/禁用 以编程方式 使用BAT or ps1
吗?以上是关于TransactionScope 过早完成的主要内容,如果未能解决你的问题,请参考以下文章
如何在多实例应用程序上处理 TransactionScope?
TransactionScope 事务 = new TransactionScope() VS TransactionScope s = context.Connection.BeginTransac