Fork me on GitHub

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

posted @ 2014-05-25 20:22  韩兆新  阅读(3470)  评论(2编辑  收藏  举报