13

这是我的事务范围源代码的当前架构。第三个插入引发 .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);
    }
}
4

6 回答 6

29

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

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

于 2009-03-05T00:24:51.840 回答
6

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

于 2008-12-04T00:48:57.690 回答
1

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

手术需要多长时间?如果它们中的任何一个运行时间很长,则可能是事务绑定 错误功能咬了你——即连接已断开。尝试添加Transaction Binding=Explicit Unbind到连接字符串。

于 2008-12-04T05:05:38.517 回答
1

只有在没有调用 ts.complete 的情况下退出 using 时才会发生隐式回滚。因为您正在处理 Insert3() 中的异常,所以异常永远不会导致 using 语句退出。

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

于 2008-12-04T00:51:56.267 回答
0

我没有看到您的帮助程序类,但如果您不调用完整语句,即使您从 .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。

异常前两次调用

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

于 2012-04-18T15:35:16.620 回答
0

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

[TransactionFlow(TransactionFlowOption.NotAllowed)]

[TransactionFlow(TransactionFlowOption.Allowed)]

于 2019-07-29T03:05:40.367 回答