基於 Transaction 類的分布式顯式事務


自.NET2.0以來增加了System.Transactions命名空間,為.NET應用程序帶來了一個新的事務編程模型。

這個命名空間提供了幾個依賴的TransactionXXX類。Transaction是所有事務處理類的基類,並且定義了所有事務類都可以使用的屬性、方法和事件。CommittableTransaction是唯一個支持提交的事務類,這個類有一個Commit()方法,所有其他事務類都只能執行回滾。

本文將通過銀行轉賬的示例介紹基於 Transaction 類的分布式顯式事務的用法。

在MySql中建立如下表:

image

注意Balance是無符號的decimal類型(如下圖)

image

插入測試數據:

(轉賬成功的測試數據):

image

(轉賬失敗的測試數據):

image

示例代碼:

(1)SqlHelper.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Configuration;
using MySql.Data.MySqlClient;
using System.Transactions;
using System.Data;
 
namespace 事務處理
{
    public class SqlHelper
    {
        public static string GetConnection()
        {
            string connStr = ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString;
            return connStr;
        }
        public static int ExecuteNonQuery(Transaction transaction,string sql,params MySqlParameter[] parameters)
        {
            int result = -1;
            using (MySqlConnection conn = new MySqlConnection(GetConnection()))
            {
                conn.Open();
                if (null != transaction)
                {
                    conn.EnlistTransaction(transaction);    //將連接登記到事務
                }
                using (MySqlCommand cmd = conn.CreateCommand())
                {
                    cmd.CommandText = sql;
                    cmd.Parameters.AddRange(parameters);
                    result = cmd.ExecuteNonQuery();
                }
            }
            return result;
        }
 
        public static DataTable ExecuteDataTable(string sql, params MySqlParameter[] parameters)
        {
            using (MySqlConnection conn = new MySqlConnection(GetConnection()))
            {
                using (MySqlCommand cmd = conn.CreateCommand())
                {
                    cmd.CommandText = sql;
                    cmd.Parameters.AddRange(parameters);
                    using (MySqlDataAdapter da = new MySqlDataAdapter(cmd))
                    {
                        using (DataSet ds = new DataSet())
                        {
                            da.Fill(ds);
                            return ds.Tables[0];
                        }
                    }
                }
            }
        }
    }
 
}

(2)Bankaccountn.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using MySql.Data.MySqlClient;
using System.Data;
using System.Transactions;
 
namespace 事務處理
{
    public class Bankaccountn
    {
        public Bankaccountn(string bankaccountnId)
        {
            string sql = @"SELECT * FROM Bankaccountn WHERE BankaccountnId=@BankaccountnId;";
            DataTable dt = SqlHelper.ExecuteDataTable(sql, new MySqlParameter("@BankaccountnId", bankaccountnId));
            if (dt.Rows.Count <= 0)
            {
                throw new Exception("賬戶不存在!");
            }
            else if (dt.Rows.Count > 1)
            {
                throw new Exception("異常信息:有重名的賬戶存在!");
            }
            else
            {
                this.bankaccountnId = dt.Rows[0]["BankaccountnId"] as string;
                this.UserName = dt.Rows[0]["UserName"] as string;
                this.Balance = Convert.ToDecimal(dt.Rows[0]["Balance"]);
            } 
        }
 
        private string bankaccountnId;
        public string UserName
        { 
            get; 
            private set; 
        }
        public decimal Balance
        {
            get;
            private set;
        }
        protected int Update(Transaction transaction)
        {
            string sql = @"UPDATE bankaccountn SET UserName = @UserName,Balance = @Balance 
                           WHERE BankaccountnId= @BankaccountnId;";
            return SqlHelper.ExecuteNonQuery(transaction, sql, new MySqlParameter("@BankaccountnId", this.bankaccountnId), new MySqlParameter("@UserName", this.UserName), new MySqlParameter("@Balance", this.Balance));
 
        }
        #region 支出 + Epend(Transaction transaction, decimal money)
        public void Epend(Transaction transaction, decimal money)
        {
            this.Balance -= money;
            this.Update(transaction);
        }
        #endregion
 
        #region 收入 + Income(Transaction transaction, decimal money)
        public void Income(Transaction transaction, decimal money)
        {
            this.Balance += money;
            this.Update(transaction);
        }
        #endregion
 
        public bool TransferOfAccount(string incomeBankaccountnId, decimal money)
        {
            using (var transaction = new CommittableTransaction())
            {
                try
                {
                    Bankaccountn incomeBankaccountn = new Bankaccountn(incomeBankaccountnId);
                    incomeBankaccountn.Income(transaction, money); //收款賬戶入賬
                    this.Epend(transaction, money); //付款賬戶支出
                    transaction.Commit();
                    return true;
                }
                catch 
                {
                    transaction.Rollback();
                    //這里寫做異常信息的記錄的代碼
                    return false; 
                }
            }
        }
    }
    
}

(3)測試代碼

Bankaccountn one = new Bankaccountn("6666660123456789");
           if (one.TransferOfAccount("6666669876543210", 200M))
           {
               Response.Write("<script>alert('轉賬成功')</script>");
           }
           else
           {
               Response.Write("<script>alert('轉賬失敗')</script>");
           }

代碼分析:

創建基於 Transaction 類的分布式顯式事務步驟如下:

1)實例化一個可提交的CommittableTransaction對象;

2)將要參與事務的連接通過MySqlConnection對象的EnlistTransaction(Transaction transaction)登記到上一步創建的CommittableTransaction對象上;

3)如果事務可以成功完成,使用CommittableTransaction對象的Commit()方法提交事務處理結果;

4)如果事務處理中發生錯誤,就調用CommittableTransaction對象的Rollback()方法,撤銷每一個修改。


這樣分析下來是不是和上一節的ADO.NET事務一樣簡單?


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM