就在不久前,我在博客園看到一個名為英雄默問出處的博客。他里面有兩篇是介紹用C#來操作事務的。用C#來操作事務和用SQL語句來操作事務原理是一模一樣的。總結起來有三個步驟:
1.開啟事務
2.判斷執行的SQL語句有沒有出錯,如果沒有就將執行完SQL語句后提交事務
3.如果有錯,那么就回滾事務
在操作事務上還分為本地事務和分布式事務。我從網上百度下來他們的定義,如下:
本地事務:將多項任務綁定在一起,使其作為單個工作單元來執行。
分布式事務:分布式事務是指事務的參與者、支持事務的服務器、資源服務器以及事務管理器分別位於不同的分布式系統的不同節點之上。
上面的定義沒看懂也沒關系,其實我看這些定義也挺煩的。記得有一回考操作系統,當時老師在課堂上說了一句可能會考定義,例如操作系統是什么,結果呢?操作系統是什么這句我背了好幾遍。臨到考前的早上還起來再讀一次。考后就忘記了。這樣的考試也夠折騰人的了。
我們可以通俗的理解成,本地事務就是操作單個數據庫的。而分布式事務就是操作多個數據庫的。
下面用三個小例子來說明一下C#怎么操作事務。
- 第一個小例子結合存儲過程和事務來操作。
- 第二個小例子使用ADO.NET里面的一個類SqlTransaction來操作本地事務。
- 第三個小例子使用TransactionScope類操作,使用TransactionScope時需添加引用命名空間,再using System.Transactions。
在看這三個小例子前先看一下數據庫用的表,為了測試,一切都非常簡單
test數據庫的bank表有哪些字段的數據
bid balance
001 400.00
002 2100.00
testLog數據庫里的bankLo有哪些字段的數據
id bid oldBalance newBalance logtime
1 002 2100.00 1100.00 2010-09-01 00:00:00.000
2 002 2100.00 1100.00 2010-09-01 00:00:00.000
第一小例子
先在數據庫里他建存儲過程,里面寫上事務的SQL語句

USE [test] GO /****** Object: StoredProcedure [dbo].[proc_bankTransaction] Script Date: 08/19/2012 10:42:05 ******/ SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO ALTER procedure [dbo].[proc_bankTransaction] ( @result int output --0表示提交事務失敗,表示提交事務成功 ) AS BEGIN declare @sumError int set @sumError=0 BEGIN TRANSACTION --開啟事務 update bank set balance=balance-100 where bid='001'; set @sumError = @sumError +@@Error update bank set balance=balance+1000 where bid='002'; set @sumError = @sumError +@@Error IF(@sumError = 0) BEGIN set @result=1 --提交事務,表示提交事物成功 COMMIT TRANSACTION END ELSE BEGIN set @result=0 --回滾事務,表示回滾事務 ROLLBACK TRANSACTION END END
C#操作的語句代碼:
static void Main(string[] args) { using (SqlConnection conn = new SqlConnection("server=.;database=test;uid=sa;pwd=123456")) { conn.Open(); using (SqlCommand cmd = new SqlCommand("proc_bankTransaction", conn)) { cmd.CommandType = CommandType.StoredProcedure; SqlParameter[] paras ={ new SqlParameter("@result",SqlDbType.Int) }; paras[0].Direction = ParameterDirection.Output; try { cmd.Parameters.AddRange(paras); cmd.ExecuteNonQuery(); Console.Write(paras[0].Value);//輸出1表示成功,輸出0表示失敗 } catch (Exception ex) { Console.WriteLine(ex.Message); } } } Console.ReadLine(); }
第二個小例子:使用ADO.NET里面的一個類SqlTransaction來操作本地事務

1 namespace ConsoleApplication1 2 { 3 class Program 4 { 5 static void Main(string[] args) 6 { 7 8 using (SqlConnection conn = new SqlConnection("server=.;database=test;uid=sa;pwd=123456")) 9 { 10 conn.Open(); 11 using (SqlCommand cmd = conn.CreateCommand()) 12 { 13 SqlTransaction transaction = conn.BeginTransaction();//開始事物 14 cmd.Transaction = transaction; 15 try 16 { 17 cmd.Connection = conn; 18 cmd.CommandText = "update bank set balance='500' where bid='001'"; 19 cmd.ExecuteNonQuery(); 20 21 cmd.CommandText = "update bank set balance='2100' where bid='002'"; 22 cmd.ExecuteNonQuery(); 23 24 transaction.Commit();//如果都成功那么提交事物 25 } 26 catch (Exception ex) 27 { 28 Console.Write(ex.Message); 29 transaction.Rollback();//出現錯誤,進行回滾 30 } 31 } 32 } 33 34 Console.ReadLine(); 35 36 37 } 38 } 39 }
第三個小例子:使用TransactionScope類操作,使用TransactionScope時需添加引用命名空間,再using System.Transactions。

1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Data; 6 using System.Data.SqlClient; 7 using System.Transactions; 8 namespace ConsoleApplication1 9 { 10 class Program 11 { 12 static void Main(string[] args) 13 { 14 15 string bankStr = "server=.;database=test;uid=sa;pwd=123456"; 16 string bankLogStr = "server=.;database=testLog;uid=sa;pwd=123456"; 17 18 //為了簡化操作步驟,假設我們已經知道是原來的balance的值為2100,更改后的值為1100 19 using (TransactionScope scope = new TransactionScope()) 20 { 21 try 22 { 23 using (SqlConnection conn = new SqlConnection(bankStr)) 24 { 25 conn.Open(); 26 using (SqlCommand cmd = conn.CreateCommand()) 27 { 28 cmd.CommandText = "update bank set balance='1100' where bid='002'"; 29 cmd.ExecuteNonQuery(); 30 } 31 } 32 33 using (SqlConnection connBankLog = new SqlConnection(bankLogStr)) 34 { 35 connBankLog.Open(); 36 using (SqlCommand cmd = connBankLog.CreateCommand()) 37 { 38 cmd.CommandText = "insert into bankLog(bid,oldBalance,newBalance,logtime) values('002','2100','1100','2010-09-01');"; 39 40 cmd.ExecuteNonQuery(); 41 } 42 } 43 scope.Complete(); 44 } 45 catch (Exception ex) 46 { 47 Console.Write(ex.Message); 48 } 49 } 50 Console.ReadLine(); 51 52 } 53 } 54 }