基于 Transaction 类的分布式显式事务

时间:2023-12-19 09:10:50

自.NET2.0以来增加了System.Transactions命名空间,为.NET应用程序带来了一个新的事务编程模型。

这个命名空间提供了几个依赖的TransactionXXX类。Transaction是所有事务处理类的基类,并且定义了所有事务类都可以使用的属性、方法和事件。CommittableTransaction是唯一个支持提交的事务类,这个类有一个Commit()方法,所有其他事务类都只能执行回滚。

本文将通过银行转账的示例介绍基于 Transaction 类的分布式显式事务的用法。

在MySql中建立如下表:

基于 Transaction 类的分布式显式事务

注意Balance是无符号的decimal类型(如下图)

基于 Transaction 类的分布式显式事务

插入测试数据:

(转账成功的测试数据):

基于 Transaction 类的分布式显式事务

(转账失败的测试数据):

基于 Transaction 类的分布式显式事务

示例代码:

(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事务一样简单?