TransactionScope 不回滚事务

发布于 2024-07-09 04:23:45 字数 1582 浏览 6 评论 0原文

这是我的事务范围源代码的当前架构。 第三个插入抛出 .NET 异常(不是 SQL 异常),并且它不会回滚前两个插入语句。 我做错了什么?

编辑:我从 insert2 和 insert3 中删除了 try/catch。 我还从 insert1 try/catch 中删除了异常处理实用程序,并放置了“throw ex”。 它仍然不会回滚事务。

编辑2:我在Insert3方法上添加了try/catch,并在catch语句中添加了“throw”。 它仍然不会回滚事务。

更新:根据我收到的反馈,“SqlHelper”类使用 SqlConnection 对象建立与数据库的连接,然后创建一个 SqlCommand 对象,将 CommandType 属性设置为“StoredProcedure”并调用SqlCommand 的 ExecuteNonQuery 方法。

我也没有将 Transaction Binding=Explicit Unbind 添加到当前连接字符串。 我会在下次测试时添加这一点。

public void InsertStuff()
{
    try
    {
        using(TransactionScope ts = new TransactionScope())
        {
            //perform insert 1
            using(SqlHelper sh = new SqlHelper())
            {
                SqlParameter[] sp = { /* create parameters for first insert */ };

                sh.Insert("MyInsert1", sp);
            }

            //perform insert 2
            this.Insert2();

            //perform insert 3 - breaks here!!!!!
            this.Insert3();

            ts.Complete();            
        }
    }
    catch(Exception ex)
    {
        throw ex;
    }
}

public void Insert2()
{
    //perform insert 2
    using(SqlHelper sh = new SqlHelper())
    {
        SqlParameter[] sp = { /* create parameters for second insert */ };

        sh.Insert("MyInsert2", sp);
    }
}

public void Insert3()
{
    //perform insert 3
    using(SqlHelper sh = new SqlHelper())
    {
        SqlParameter[] sp = { /*create parameters for third insert */ };

        sh.Insert("MyInsert3", sp);
    }
}

Here is the current architecture of my transaction scope source code. The third insert throws an .NET exception (Not a SQL Exception) and it is not rolling back the two previous insert statements. What I am doing wrong?

EDIT: I removed the try/catch from insert2 and insert3. I also removed the exception handling utility from the insert1 try/catch and put "throw ex". It still does not rollback the transaction.

EDIT 2: I added the try/catch back on the Insert3 method and just put a "throw" in the catch statement. It still does not rollback the transaction.

UPDATE:Based on the feedback I received, the "SqlHelper" class is using the SqlConnection object to establish a connection to the database, then creates a SqlCommand object, set the CommandType property to "StoredProcedure" and calls the ExecuteNonQuery method of the SqlCommand.

I also did not add Transaction Binding=Explicit Unbind to the current connection string. I will add that during my next test.

public void InsertStuff()
{
    try
    {
        using(TransactionScope ts = new TransactionScope())
        {
            //perform insert 1
            using(SqlHelper sh = new SqlHelper())
            {
                SqlParameter[] sp = { /* create parameters for first insert */ };

                sh.Insert("MyInsert1", sp);
            }

            //perform insert 2
            this.Insert2();

            //perform insert 3 - breaks here!!!!!
            this.Insert3();

            ts.Complete();            
        }
    }
    catch(Exception ex)
    {
        throw ex;
    }
}

public void Insert2()
{
    //perform insert 2
    using(SqlHelper sh = new SqlHelper())
    {
        SqlParameter[] sp = { /* create parameters for second insert */ };

        sh.Insert("MyInsert2", sp);
    }
}

public void Insert3()
{
    //perform insert 3
    using(SqlHelper sh = new SqlHelper())
    {
        SqlParameter[] sp = { /*create parameters for third insert */ };

        sh.Insert("MyInsert3", sp);
    }
}

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

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

发布评论

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

评论(6

疑心病 2024-07-16 04:23:45

我也遇到了类似的问题。 出现问题是因为我在 SqlCommands 中使用的 SqlConnection 在创建 TransactionScope 之前就已打开,因此它从未作为事务列入 TransactionScope 中。

SqlHelper 类是否可能重用在您进入 TransactionScope 块之前打开的 SqlConnection 实例?

I have also run into a similar issue. My problem occurred because the SqlConnection I used in my SqlCommands was already open before the TransactionScope was created, so it never got enlisted in the TransactionScope as a transaction.

Is it possible that the SqlHelper class is reusing an instance of SqlConnection that is open before you enter your TransactionScope block?

寂寞陪衬 2024-07-16 04:23:45

看起来您正在捕获 Insert3() 中的异常,因此您的代码在调用后继续。 如果您希望它回滚,您需要让异常冒泡到主例程中的 try/catch 块,以便永远不会调用 ts.Complete() 语句。

It looks like you are catching the exception in Insert3() so your code continues after the call. If you want it to rollback you'll need to let the exception bubble up to the try/catch block in the main routine so that the ts.Complete() statement never gets called.

冷清清 2024-07-16 04:23:45

仅当退出 using 且未调用 ts.complete 时,才会发生隐式回滚。 因为您正在 Insert3() 中处理异常,所以该异常永远不会导致 using 语句退出。

要么重新抛出异常,要么通知调用者需要回滚(将 Insert3() 的签名更改为 bool Insert3()?)

An implicit rollback will only occur if the using is exited without calling ts.complete. Because you are handling the exception in Insert3() the exception never causes an the using statement to exit.

Either rethrow the exception or notify the caller that a rollback is needed (make change the signature of Insert3() to bool Insert3()?)

站稳脚跟 2024-07-16 04:23:45

(基于不吞咽异常的编辑版本)

操作需要多长时间? 如果其中任何一个运行时间很长,则事务绑定 bug 功能已经困扰您 - 即连接已断开。 尝试将 Transaction Binding=Explicit Unbind 添加到连接字符串。

(based on the edited version that doesn't swallow exceptions)

How long do the operations take? If any of them are very long running, it is possible that the Transaction Binding bug feature has bitten you - i.e. the connection has become detached. Try adding Transaction Binding=Explicit Unbind to the connection string.

南冥有猫 2024-07-16 04:23:45

我没有看到您的帮助程序类,但如果您不调用完整语句,即使您从 .NET 代码中收到错误,事务范围也会回滚。 我给你复制了一个例子。 您可能在调试时做错了什么。 此示例在 .net 代码中存在错误,并且与您的类似的 catch 块。

  private static readonly string _connectionString = ConnectionString.GetDbConnection();

    private const string inserttStr = @"INSERT INTO dbo.testTable (col1) VALUES(@test);";

        /// <summary>
        /// Execute command on DBMS.
        /// </summary>
        /// <param name="command">Command to execute.</param>
        private void ExecuteNonQuery(IDbCommand command)
        {
            if (command == null)
                throw new ArgumentNullException("Parameter 'command' can't be null!");

            using (IDbConnection connection = new SqlConnection(_connectionString))
            {
                command.Connection = connection;
                connection.Open();
                command.ExecuteNonQuery();
            }
        }

        public void FirstMethod()
        {
            IDbCommand command = new SqlCommand(inserttStr);
            command.Parameters.Add(new SqlParameter("@test", "Hello1"));


                ExecuteNonQuery(command);

        }

        public void SecondMethod()
        {
            IDbCommand command = new SqlCommand(inserttStr);
            command.Parameters.Add(new SqlParameter("@test", "Hello2"));


                ExecuteNonQuery(command);

        }

        public void ThirdMethodCauseNetException()
        {
            IDbCommand command = new SqlCommand(inserttStr);
            command.Parameters.Add(new SqlParameter("@test", "Hello3"));


                ExecuteNonQuery(command);
            int a = 0;
            int b = 1/a;

        }

    public void MainWrap()
    {


        TransactionOptions tso = new TransactionOptions();
        tso.IsolationLevel = System.Transactions.IsolationLevel.ReadCommitted;
        //TransactionScopeOption.Required, tso
        try
        {
            using (TransactionScope sc = new TransactionScope())
            {
                FirstMethod();
                SecondMethod();
                ThirdMethodCauseNetException();
                sc.Complete();
            }
        }
        catch (Exception ex)
        {
            logger.ErrorException("eee ",ex);

        }
    }

如果你想调试你的事务,你可以使用这个脚本来查看锁和等待状态等。

SELECT 
request_session_id AS spid,
CASE transaction_isolation_level 
WHEN 0 THEN 'Unspecified' 
WHEN 1 THEN 'ReadUncomitted' 
WHEN 2 THEN 'Readcomitted' 
WHEN 3 THEN 'Repeatable' 
WHEN 4 THEN 'Serializable' 
WHEN 5 THEN 'Snapshot' END AS TRANSACTION_ISOLATION_LEVEL ,
resource_type AS restype,
resource_database_id AS dbid,
DB_NAME(resource_database_id) as DBNAME,
resource_description AS res,
resource_associated_entity_id AS resid,
CASE 
when resource_type = 'OBJECT' then OBJECT_NAME( resource_associated_entity_id) 
ELSE 'N/A'
END as ObjectName,
request_mode AS mode,
request_status AS status
FROM sys.dm_tran_locks l
left join sys.dm_exec_sessions s on l.request_session_id = s.session_id
where resource_database_id = 24
order by spid, restype, dbname;

在调用异常方法之前,你将看到两个方法调用的一个SPID。

异常前两次调用

默认隔离级别是可序列化的。您可以在此处阅读有关锁和事务的更多信息

I dont see your helper class, but transaction scope rollsback if you don't call complete statement even if you get error from .NET code. I copied one example for you. You may be doing something wrong in debugging. This example has error in .net code and similar catch block as yours.

  private static readonly string _connectionString = ConnectionString.GetDbConnection();

    private const string inserttStr = @"INSERT INTO dbo.testTable (col1) VALUES(@test);";

        /// <summary>
        /// Execute command on DBMS.
        /// </summary>
        /// <param name="command">Command to execute.</param>
        private void ExecuteNonQuery(IDbCommand command)
        {
            if (command == null)
                throw new ArgumentNullException("Parameter 'command' can't be null!");

            using (IDbConnection connection = new SqlConnection(_connectionString))
            {
                command.Connection = connection;
                connection.Open();
                command.ExecuteNonQuery();
            }
        }

        public void FirstMethod()
        {
            IDbCommand command = new SqlCommand(inserttStr);
            command.Parameters.Add(new SqlParameter("@test", "Hello1"));


                ExecuteNonQuery(command);

        }

        public void SecondMethod()
        {
            IDbCommand command = new SqlCommand(inserttStr);
            command.Parameters.Add(new SqlParameter("@test", "Hello2"));


                ExecuteNonQuery(command);

        }

        public void ThirdMethodCauseNetException()
        {
            IDbCommand command = new SqlCommand(inserttStr);
            command.Parameters.Add(new SqlParameter("@test", "Hello3"));


                ExecuteNonQuery(command);
            int a = 0;
            int b = 1/a;

        }

    public void MainWrap()
    {


        TransactionOptions tso = new TransactionOptions();
        tso.IsolationLevel = System.Transactions.IsolationLevel.ReadCommitted;
        //TransactionScopeOption.Required, tso
        try
        {
            using (TransactionScope sc = new TransactionScope())
            {
                FirstMethod();
                SecondMethod();
                ThirdMethodCauseNetException();
                sc.Complete();
            }
        }
        catch (Exception ex)
        {
            logger.ErrorException("eee ",ex);

        }
    }

If you want to debug your transactions, you can use this script to see locks and waiting status etc.

SELECT 
request_session_id AS spid,
CASE transaction_isolation_level 
WHEN 0 THEN 'Unspecified' 
WHEN 1 THEN 'ReadUncomitted' 
WHEN 2 THEN 'Readcomitted' 
WHEN 3 THEN 'Repeatable' 
WHEN 4 THEN 'Serializable' 
WHEN 5 THEN 'Snapshot' END AS TRANSACTION_ISOLATION_LEVEL ,
resource_type AS restype,
resource_database_id AS dbid,
DB_NAME(resource_database_id) as DBNAME,
resource_description AS res,
resource_associated_entity_id AS resid,
CASE 
when resource_type = 'OBJECT' then OBJECT_NAME( resource_associated_entity_id) 
ELSE 'N/A'
END as ObjectName,
request_mode AS mode,
request_status AS status
FROM sys.dm_tran_locks l
left join sys.dm_exec_sessions s on l.request_session_id = s.session_id
where resource_database_id = 24
order by spid, restype, dbname;

You will see one SPID for two method calls before calling exception method.

two calls before exception

Default isolation level is serializable.You can read more about locks and transactions here

爱的故事 2024-07-16 04:23:45

当我在 TransactionScope 中调用 WCF 服务操作时,我遇到了类似的问题。
我注意到由于服务接口中的“TransactionFlow”属性,不允许交易流。 因此,WCF 服务操作没有使用外部事务范围所使用的事务。 将其更改为允许交易流程,如下所示解决了我的问题。

[TransactionFlow(TransactionFlowOption.NotAllowed)]

[TransactionFlow(TransactionFlowOption.Allowed)]

I ran into a similar issue when I had a call to a WCF service operation in TransactionScope.
I noticed transaction flow was not allowed due to the 'TransactionFlow' attribute in the service interface. Therefore, the WCF service operation was not using the transaction used by the outer transaction scope. Changing it to allow transaction flow as shown below fixed my problem.

[TransactionFlow(TransactionFlowOption.NotAllowed)]

to

[TransactionFlow(TransactionFlowOption.Allowed)]

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