【.NET】使用 TransactionScope 建立 SQL 分散式交易

  • 20763
  • 0

  在專案需要進行 SQL 分散式交易的時候,使用 TransactionScope 是一個不錯的選擇。

  要使用 TransactionScope 進行分散式交易,除了程式碼之外,環境也要作相對應的設定,可參考小弟文章:【Windows】啟用 MSDTC 服務與相關設定

1、情境設計

  有兩個位於不同資料庫主機的資料庫,其中都有一個名為【Student】的資料表,資料結構完全相同,Id 為主索引。

  希望有個 Move 的方法,傳入 StudentId 時,將此筆 Student 資料由來源資料庫複製至目標資料庫,並刪除來源資料庫的 Student 資料,最後回傳搬移結果。

2、TransactionScope 實作

  在命名空間 System.Transactions 下的 TransactionScope 類別,是 .NET 用來包覆交易式程式碼區塊的類別。

  在 TransactionScope 範圍內的資料庫操作均會受到交易保護。若範圍內只針對單一資料庫進行操作,則與使用 ADO.NET 一樣;若範圍內針對多個不同資料庫進行操作,則會被提升成為分散式交易。

  底下程式碼使用 Nuget 套件:Dapper 進行實作。

using Dapper;
using Model;
using System.Data.SqlClient;
using System.Linq;
using System.Transactions;

namespace Service
{
    public sealed class TransactionService
    {
        private readonly string sourceConnectionString;
        private readonly string targetConnectionString;

        public TransactionService(string sourceConnectionString, string targetConnectionString)
        {
            this.sourceConnectionString = sourceConnectionString;
            this.targetConnectionString = targetConnectionString;
        }

        public bool MoveStudent(int studentId)
        {
            try
            {
                using (var scope = new TransactionScope())
                {
                    var student = new Student();

                    using (var sqlConn = new SqlConnection(this.sourceConnectionString))
                    {
                        var students = sqlConn.Query<Student>(@"SELECT [Id], [Name] FROM [Student] WHERE [Id] = @Id", new { Id = studentId });

                        if (students == null || students.Count() != 1)
                        {
                            return false;
                        }

                        student = students.Single();

                        sqlConn.Execute(@"DELETE FROM [Student] WHERE [Id] = @Id AND [Name] = @Name", student);
                    }

                    using (var sqlConn = new SqlConnection(this.targetConnectionString))
                    {
                        sqlConn.Execute(@"INSERT INTO [Student]([Id], [Name]) VALUES (@Id, @Name)", student);
                    }

                    // 若在 complete 前發生例外,包含在 scope 內的交易將會被 roll back
                    scope.Complete();

                    return true;
                }
            }
            catch
            {
                return false;
            }
        }
    }
}

嘗試將自己的理解寫成文字紀錄,資料來源均來自於網路。

如有理解錯誤、引用錯誤或侵權,請多加指正與告知,讓我有更多的進步與改進的空間,謝謝!