TransactionScope过早完成

Chr*_*ris 64 c# sql-server ado.net deadlock transactionscope

我有一个在TransactionScope中运行的代码块,在这段代码中,我多次调用DB.选择,更新,创建和删除整个色域.当我执行删除操作时,我使用SqlCommand的扩展方法执行它,如果查询死锁,它将自动重新提交查询,因为此查询可能会遇到死锁.

我相信当遇到死锁并且函数尝试重新提交查询时会发生此问题.这是我收到的错误:

与当前连接关联的事务已完成但尚未处理.必须先处理事务,然后才能使用连接执行SQL语句.

这是执行查询的简单代码(下面的所有代码都在使用TransactionScope执行):

using (sqlCommand.Connection = new SqlConnection(ConnectionStrings.App))
{
    sqlCommand.Connection.Open();
    sqlCommand.ExecuteNonQueryWithDeadlockHandling();
}
Run Code Online (Sandbox Code Playgroud)

以下是重新提交死锁查询的扩展方法:

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

该行发生错误:

sqlCommand.ExecuteNonQuery();
Run Code Online (Sandbox Code Playgroud)

Dan*_*Dan 60

不要忘记从TransactionScope中压缩select语句.在SQL Server 2005及更高版本中,即使使用(nolock),仍然会在这些表上创建锁定选择触摸.看看这个,它会告诉你如何设置和使用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 
  } 
} 
Run Code Online (Sandbox Code Playgroud)

  • 很棒的答案.这让我对一系列sql指令的选择/插入集合感到疯狂.添加"抑制"选项会自动解决问题. (2认同)
  • 过热的锁定通常是默认的`Serializable` TS隔离级别的结果.这通常最好通过将TS创建包装在classfactory中来解决,该类将序列化设置为更像[Read Read Comitted](http://stackoverflow.com/questions/11292763/why-is-system-transactions-transactionscope-default-isolationlevel可序列化) (2认同)

Mar*_*cus 42

我发现当事务运行的时间比maxTimeoutfor 更长时,可能会出现此消息System.Transactions.无所谓TransactionOptions.Timeout增加,它不能超过maxTimeout.

默认值maxTimeout设置为10分钟,其值只能在.中修改machine.config

将以下内容(在配置级别中)添加machine.config到修改超时:

<configuration>
    <system.transactions>
        <machineSettings maxTimeout="00:30:00" />
    </system.transactions>
</configuration>
Run Code Online (Sandbox Code Playgroud)

machine.config可以在以下位置找到: %windir%\Microsoft.NET\Framework\[version]\config\machine.config

您可以在此博客文章中阅读更多相关信息:http://thecodesaysitall.blogspot.se/2012/04/long-running-systemtransactions.html

  • 请记住,配置文件区分大小写,因此应该是"machineSettings"和"maxTimeout".太糟糕了,你不能在你的app.config文件中覆盖这个:( (3认同)

Rol*_*olf 21

我可以重现这个问题.这是一个事务超时.

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())
                    {
                    }
                }
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

抛出System.InvalidOperationException此消息:

与当前连接关联的事务已完成但尚未处理.必须先处理事务,然后才能使用连接执行SQL语句.

要解决此问题,请使查询运行得更快或增加超时.


Don*_*nie 11

如果内部发生异常TransactionScope,则会回滚.这意味着TransactionScope完成了.您现在必须调用dispose()它并开始新的事务.老实说,我不确定你是否可以重复使用旧的TransactionScope,我从未尝试过,但我不认为.

  • 即使捕获到异常,事务也会被回滚? (2认同)
  • 这是错误的,不是异常发生的情况。您也不必再调用Dispose()。在using语句中生成TransactionScope时,using语句将在出现异常时Dispose()TransactionScope。 (2认同)

小智 6

我的问题是一个愚蠢的问题,如果你坐在调试中断超时,你会得到这个.面掌

男人,编程让你有些日子感到厚重......


Stu*_*tLC 6

确认此错误也可能由事务超时引起.只是为了补充Marcus + Rolf所说的内容,如果你没有明确设置超时 TransactionScope,TimeSpan的超时将采用默认值.此默认值是以下值中的较小值:

  1. 如果你已经覆盖了本地app.config/ web.config设置,例如

    <system.transactions>
    <defaultSettings timeout="00:05:00" />
    </system.transactions>
    
    Run Code Online (Sandbox Code Playgroud)
  2. 但是,这会被machine.config设置为"上限"<machineSettings maxTimeout="00:10:00" />