自.NET2.0以來增加了System.Transactions命名空間,為.NET應用程序帶來了一個新的事務編程模型。
這個命名空間提供了幾個依賴的TransactionXXX類。Transaction是所有事務處理類的基類,並且定義了所有事務類都可以使用的屬性、方法和事件。CommittableTransaction是唯一個支持提交的事務類,這個類有一個Commit()方法,所有其他事務類都只能執行回滾。
本文將通過銀行轉賬的示例介紹基於 Transaction 類的分布式顯式事務的用法。
在MySql中建立如下表:
注意Balance是無符號的decimal類型(如下圖)
插入測試數據:
(轉賬成功的測試數據):
(轉賬失敗的測試數據):
示例代碼:
(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事務一樣簡單?