Quantcast
Channel: 黑暗執行緒
Viewing all articles
Browse latest Browse all 2447

TransactionScope與COMMIT TRAN

$
0
0

手邊的專案遇到一個情境: TransactionScope中包含兩段SQL操作,因未共用連線,預期將啟動MSDTC分散式交易。而第二段SQL操作使用了T-SQL的BEGIN TRAN與COMMIT TRAN。若TrasactionScope未能呼叫TransactionScope.Complete(),第二段COMMIT TRAN的DB寫入動作是否會Rollback? 這個議題,引起討論。

依我的理解,BEGIN TRAN/COMMIT TRAN形成的子交易,仍會參與當下的環境交易(Ambient Transaction,包在using TransactionScope範圍的資料庫操作,預設會參與該TransactionScope所建立的交易,該交易即所謂環境交易[參考]),當環境交易Rollback(亦即TransactionScope未呼叫Complete()),子交易將會跟著Rollback。

由於信仰不夠堅定,無法100%確認自己的認知無誤,故設計實驗進行驗證:

using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Linq;
using System.Text;
using System.Transactions;
 
namespace TestTSqlCommit
{
class Program
    {
staticvoid truncateTable()
        {
using (SqlConnection cn = new SqlConnection(getDiffCnStr()))
            {
                cn.Open();
                var cmd = cn.CreateCommand();
                cmd.CommandText = "TRUNCATE TABLE LockLab";
                cmd.ExecuteNonQuery();
            }
        }
 
 
staticstring cs = "Data Source=...省略...;Application Name={0}";
//使用隨機Application Name建立連線字串
staticstring getDiffCnStr()
        {
returnstring.Format(cs, Guid.NewGuid());
        }
 
staticvoid Main(string[] args)
        {
//將LockLab資料表清空
            truncateTable();
//建立TransactionScope
using (TransactionScope tx = new TransactionScope())
            {
try
                {
//以隨機式連線字串建立連線,確保觸發分散式交易
//參考: http://blog.darkthread.net/post-2010-11-12-msdtc-2008.aspx
using (SqlConnection cn = new SqlConnection(getDiffCnStr()))
                    {
                        cn.Open();
                        var cmd = cn.CreateCommand();
                        cmd.CommandText = 
"INSERT INTO LockLab VALUES ('TEST','201302', 1)";
                        cmd.ExecuteNonQuery();
                    }
//第二段SQL 使用COMMIT TRAN
using (SqlConnection cn = new SqlConnection(getDiffCnStr()))
                    {
                        cn.Open();
                        var cmd = cn.CreateCommand();
                        cmd.CommandText = @"
BEGIN TRAN
INSERT INTO LockLab VALUES ('JEFF','201302', 1)
COMMIT TRAN
";
                        cmd.ExecuteNonQuery();
                    }
 
thrownew ApplicationException("刻意觸發錯誤!");
//tx.Complete()不會被執行,Transaction應Rollback
                    tx.Complete();
                }
catch (Exception ex)
                {
                    Console.Write(ex.ToString());
                }
            }
            Console.Read();
        }
    }
}

測試結果如同預期,TEST與JEFF兩筆資料均未寫入,證實TransactionScope中即便BEGIN TRAN/COMMIT TRAN仍會參與交易,可被Rollback。

最後補充,如果我們希望子交易不要依附於外層TransactionScope,以實現不論外層Transaction Rollback與否,子交易需能自由控制是否Commit的結果。為此,我們可宣告一個TransactionScopeOption.RequiresNew模式的TransactionScope將子交易包起來,則其中將自成一個獨立交易範圍,不受環境交易影響。沿用上例,如要將第二段獨立自成交易,可修改為:

using (TransactionScope tx2 = 
newTransactionScope(TransactionScopeOption.RequiresNew))
                    {
using (SqlConnection cn = new SqlConnection(getDiffCnStr()))
                        {
                            cn.Open();
                            var cmd = cn.CreateCommand();
                            cmd.CommandText = @"
BEGIN TRAN
INSERT INTO LockLab VALUES ('JEFF','201302', 1)
COMMIT TRAN
";
                            cmd.ExecuteNonQuery();
                        }
tx2.Complete();
                    }

如此,即便tx.Complete()未執行,JEFF資料也會寫入DB。


Viewing all articles
Browse latest Browse all 2447

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>