TransactionScope 提前完成

发布于 2024-09-03 01:15:29 字数 1662 浏览 5 评论 0原文

我有一个在 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();

I have a block of code that runs within a TransactionScope and within this block of code I make several calls to the DB. Selects, Updates, Creates, and Deletes, the whole gamut. When I execute my delete I execute it using an extension method of the SqlCommand that will automatically resubmit the query if it deadlocks as this query could potentially hit a deadlock.

I believe the problem occurs when a deadlock is hit and the function tries to resubmit the query. This is the error I receive:

The transaction associated with the current connection has completed but has not been disposed. The transaction must be disposed before the connection can be used to execute SQL statements.

This is the simple code that executes the query (all of the code below executes within the using of the TransactionScope):

using (sqlCommand.Connection = new SqlConnection(ConnectionStrings.App))
{
    sqlCommand.Connection.Open();
    sqlCommand.ExecuteNonQueryWithDeadlockHandling();
}

Here is the extension method that resubmits the deadlocked query:

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;
    }
}

The error occurs on the line:

sqlCommand.ExecuteNonQuery();

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

如歌彻婉言 2024-09-10 01:15:29

不要忘记抑制 TransactionScope 中的 select 语句。在 SQL Server 2005 及更高版本中,即使您使用 with(nolock),仍然会在 select 涉及的那些表上创建锁。看看这个,它向您展示了如何设置和使用 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 
  } 
} 

Don't forget to supress your select statements from your TransactionScope. In SQL Server 2005 and above, even when you use with(nolock), locks are still created on those tables the select touches. Check this out, it shows you 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 
  } 
} 
伏妖词 2024-09-10 01:15:29

我发现当事务运行时间长于 System.TransactionsmaxTimeout 时,可能会出现此消息。 TransactionOptions.Timeout增加没关系,它不能超过maxTimeout

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

添加以下内容(在配置中) level) 到 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

I've found that this message can occur when a transaction runs for a longer period than the maxTimeout for System.Transactions. It doesn't matter that TransactionOptions.Timeout is increased, it can't exceed maxTimeout.

The default value of maxTimeout is set to 10 minutes and its value can only be modified in the machine.config

Add the following (in the configuration level) to the machine.config to modify the timeout:

<configuration>
    <system.transactions>
        <machineSettings maxTimeout="00:30:00" />
    </system.transactions>
</configuration>

The machine.config can be found at: %windir%\Microsoft.NET\Framework\[version]\config\machine.config

You can read more about it in this blog post: http://thecodesaysitall.blogspot.se/2012/04/long-running-systemtransactions.html

倾城°AllureLove 2024-09-10 01:15:29

我可以重现该问题。这是事务超时。

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语句。

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

I can reproduce the problem. It is a transaction timeout.

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())
                    {
                    }
                }
            }
        }
    }
}

Throws System.InvalidOperationException with this message:

The transaction associated with the current connection has completed
but has not been disposed. The transaction must be disposed before
the connection can be used to execute SQL statements.

To solve the problem make your query run faster or increase the timeout.

森罗 2024-09-10 01:15:29

如果在 TransactionScope 内发生异常,则会回滚。这意味着 TransactionScope 已完成。您现在必须对其调用 dispose() 并启动新事务。老实说,我不确定您是否可以重用旧的 TransactionScope ,我从未尝试过,但我认为不能。

If an exception happens inside a TransactionScope it is rolled back. This means that TransactionScope is done. You must now call dispose() on it and start a new transaction. I'm honestly not sure if you can reuse the old TransactionScope or not, I've never tried, but I'd assume not.

圈圈圆圆圈圈 2024-09-10 01:15:29

我的问题是一个愚蠢的问题,如果你在超时时进行调试突破,你就会得到这个。 Face Palm

伙计,编程有时会让你感觉很厚重……

My issue was a stupid one, if you sit on a debug break through the timeout you will get this. Face Palm

Man, programming makes you feel thick some days...

善良天后 2024-09-10 01:15:29

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

  1. 如果您已覆盖本地 app.config / web.config 设置,例如< /p>

    ;
    
    
    
  2. But this is then 'capped' at machine.config 设置

Confirmed this error can also be caused by a transaction timeout. Just to add to what Marcus + Rolf have stated, if you haven't explicitly set a timeout on the TransactionScope, the timeout TimeSpan will assume a default value. This default value is the smaller of:

  1. If you've overridden the local app.config / web.config setting, e.g.

    <system.transactions>
    <defaultSettings timeout="00:05:00" />
    </system.transactions>
    
  2. But this is then 'capped' at the machine.config setting <machineSettings maxTimeout="00:10:00" />

熊抱啵儿 2024-09-10 01:15:29

此异常也可能是由禁用 Microsoft 分布式事务协调器 引起的。

如果我们想启用它,我们运行“dcomcnfg”并选择“组件服务”-> “我的电脑”-> 《分布式事务协调器》-> “本地服务 DTC” 并选择“属性”。

应选中“允许远程客户端”、“允许入站”、“允许出站”和“无需身份验证”强>”。

This exception can also be caused by disable Microsoft Distributed Transaction Coordinator.

If we want enable it, we run "dcomcnfg" and select "Component Services" -> "My Computer" -> "Distributed Transaction Coordinator" -> "Local Service DTC" and choose "Properties".

It should be checked "Allow Remote Client", "Allow Inbound", "Allow Outbound" and "No Authentication Required".

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文