在一個MIS系統中,沒有用事務那就絕對是有問題的,要么就只有一種情況:你的系統實在是太小了,業務業務邏輯有只要一步執行就可以完成了。因此掌握事務處理的方法是很重要,進我的歸類在.net中大致有以下4種事務處理的方法。大家可以參考一下,根據實際選擇適當的事務處理。
1 SQL事務
sql事務是使用SQL server自身的事務:在存儲過程中直接使用Begin Tran,Rollback Tran,Commit Tran實現事務:
優點:執行效率最佳
限制:事務上下文僅在數據庫中調用,難以實現復雜的業務邏輯。
Demo:(所有demo,都以SQL Server自帶的Northwind數據的表Region為例)
CREATE PROCEDURE dbo.SPTransaction
(
@UpdateID int,
@UpdateValue nchar(50),
@InsertID int,
@InsertValue nchar(50)
)
AS
begin Tran
Update Region Set RegionDescription=@UpdateValue where RegionID=@UpdateID

insert into Region Values (@InsertID,@InsertValue)

declare @RegionError int
select @RegionError=@@error
if(@RegionError=0)
COMMIT Tran
else
ROLLBACK Tran
GO
執行帶事務的存儲過程
2 ADO.net事務
Ado.net事務可能是大家一般都用的
優點:簡單,效率和數據庫事務差不多。
缺點:事務不能跨數據庫,只能在一個數據庫連接上。如果是兩個數據庫上就不能使用該事務了。
Demo:
/// <summary>
/// 一般的ADO.net 事務
/// </summary>
public void ADONetTran1()
{
SqlConnection conn = new SqlConnection("Data Source=127.0.0.1;Initial Catalog=Northwind;Persist Security Info=True;User ID=sa;Password=123;");
SqlCommand cmd = new SqlCommand();
try
{
cmd.CommandText = "Update Region Set RegionDescription=@UpdateValue where RegionID=@UpdateID";
cmd.CommandType = CommandType.Text;
cmd.Connection = conn;
conn.Open();
SqlParameter[] paras = new SqlParameter[]{
new SqlParameter ("@UpdateID",SqlDbType.Int,32),
new SqlParameter ("@UpdateValue",SqlDbType .NChar,50)};
paras[0].Value = "2";
paras[1].Value = "Update Value12";

foreach (SqlParameter para in paras)
{
cmd.Parameters.Add(para);
}
//開始事務
cmd.Transaction = conn.BeginTransaction();
cmd.ExecuteNonQuery();


cmd.CommandText = "insert into Region values(@InsertID,@InsertValue)";
cmd.CommandType = CommandType.Text;

paras = new SqlParameter[]{
new SqlParameter ("@InsertID",SqlDbType.Int ,32),
new SqlParameter ("@InsertValue",SqlDbType.NChar ,50)};
paras[0].Value = "7";
paras[1].Value = "Insert Value";

cmd.Parameters.Clear();
foreach (SqlParameter para in paras)
{
cmd.Parameters.Add(para);
}
cmd.ExecuteNonQuery();
//提交事務
cmd.Transaction.Commit();
}
catch
{
//回滾事務
cmd.Transaction.Rollback();
throw;
}
finally
{
conn.Close();
}

}
3 TransactionScope事務
TransactionScope事務類,它可以使代碼塊成為事務性代碼。並自動提升為分布式事務
優點:實現簡單,同時能夠自動提升為分布式事務
Demo:
/// <summary>
/// TransactionScope事務:可自動提升事務為完全分布式事務的輕型(本地)事務。
/// 使用時要保證MSDTC服務(控制分布事務)是開啟的可以使用:net start msdtc命令開啟服務;
/// </summary>
public void ADONetTran2()
{
SqlConnection conn = new SqlConnection("Data Source=127.0.0.1;Initial Catalog=Northwind;Persist Security Info=True;User ID=sa;Password=123;");
SqlCommand cmd = new SqlCommand();
try
{
using (System.Transactions.TransactionScope ts = new TransactionScope())
{
cmd.CommandText = "Update Region Set RegionDescription=@UpdateValue where RegionID=@UpdateID";
cmd.CommandType = CommandType.Text;
cmd.Connection = conn;
conn.Open();
SqlParameter[] paras = new SqlParameter[]{
new SqlParameter ("@UpdateID",SqlDbType.Int,32),
new SqlParameter ("@UpdateValue",SqlDbType .NChar,50)};
paras[0].Value = "2";
paras[1].Value = "Update Value12";

foreach (SqlParameter para in paras)
{
cmd.Parameters.Add(para);
}
cmd.ExecuteNonQuery();


cmd.CommandText = "insert into Region values(@InsertID,@InsertValue)";
cmd.CommandType = CommandType.Text;

paras = new SqlParameter[]{
new SqlParameter ("@InsertID",SqlDbType.Int ,32),
new SqlParameter ("@InsertValue",SqlDbType.NChar ,50)};
paras[0].Value = "8";
paras[1].Value = "Insert Value";

cmd.Parameters.Clear();
foreach (SqlParameter para in paras)
{
cmd.Parameters.Add(para);
}

cmd.ExecuteNonQuery();
//提交事務
ts.Complete();
}
}
catch
{
throw;
}
finally
{
conn.Close();
}

}
4 COM+事務
在分布式應用程序中,往往要同時操作多個數據庫,使用數據庫事務就不能滿足業務的要求了。在COM+中,提供完整的事務處理服務。很方便處理多個數據庫上的事務。
Demo:
/// <summary>
/// COM+事務
/// </summary>
public void ComTran()
{
SqlConnection conn = new SqlConnection("Data Source=127.0.0.1;Initial Catalog=Northwind;Persist Security Info=True;User ID=sa;Password=123;");
SqlCommand cmd = new SqlCommand();
ServiceConfig sc = new ServiceConfig();

//指定事務類型
sc.Transaction = TransactionOption.Required;
//設置啟動跟蹤
sc.TrackingEnabled = true;
//創建一個上下文,該上下文的配置由作為 cfg 參數傳遞的 ServiceConfig 對象來指定。
//隨后,客戶端和服務器端的策略均被觸發,如同發生了一個方法調用。
//接着,新的上下文被推至上下文堆棧,成為當前上下文
ServiceDomain.Enter(sc);
try
{
cmd.CommandText = "Update Region Set RegionDescription=@UpdateValue where RegionID=@UpdateID";
cmd.CommandType = CommandType.Text;
cmd.Connection = conn;
conn.Open();
SqlParameter[] paras = new SqlParameter[]{
new SqlParameter ("@UpdateID",SqlDbType.Int,32),
new SqlParameter ("@UpdateValue",SqlDbType .NChar,50)};
paras[0].Value = "2";
paras[1].Value = "Update Value22";

foreach (SqlParameter para in paras)
{
cmd.Parameters.Add(para);
}
cmd.ExecuteNonQuery();


cmd.CommandText = "insert into Region values(@InsertID,@InsertValue)";
cmd.CommandType = CommandType.Text;

paras = new SqlParameter[]{
new SqlParameter ("@InsertID",SqlDbType.Int ,32),
new SqlParameter ("@InsertValue",SqlDbType.NChar ,50)};
paras[0].Value = "9";
paras[1].Value = "Insert Value";

cmd.Parameters.Clear();
foreach (SqlParameter para in paras)
{
cmd.Parameters.Add(para);
}

cmd.ExecuteNonQuery();

//提交事務
ContextUtil.SetComplete();
}
catch
{
//回滾事務
ContextUtil.SetAbort();
throw;
}
finally
{
conn.Close();
//觸發服務器端的策略,隨后觸發客戶端的策略,如同一個方法調用正在返回。
//然后,當前上下文被彈出上下文堆棧,調用 Enter 時正在運行的上下文成為當前的上下文。
ServiceDomain.Leave();
}

}
在.net中還有些也能進行事務處理,如web Service中
需要特別補充的是:
如果你使用的是分布事務(TransactionScope事務和COM+事務),在默認情況下你是要重新配置安裝SQL Server數據庫服務器和訪問數據庫的客戶端的.(如果沒有配置運行會出現以下錯誤:該伙伴事務管理器已經禁止了它對遠程/網絡事務的支持。 (異常來自 HRESULT:0x8004D025)
)下面是MSDN上關於配置分布式事務的一段原話:
配置分布式事務
要啟用分布式事務,可能需要通過網絡啟用 MS DTC,以便在使用應用了最新的 Service Pack 的較新操作系統(例如 Windows XP 或 Windows 2003)時使用分布式事務。如果啟用了 Windows 防火牆(Windows XP Service Pack 2 的默認設置),必須允許 MS DTC 服務使用網絡或打開 MS DTC 端口。
實際怎么配置呢,經過我的實際使用:大致如下:打開'控制面板'->'管理工具'->'組件服務',點開'組件服務'->'計算機'->'我的電腦',在'我的電腦'上右擊屬性,點'MSDTC',然后點'安全性配置'。作為數據庫的服務器的配置如下:

而訪問數據庫的客戶端的配置和服務器端的稍有些差別:

在設置完上面的還有使防火牆MS DTC 服務使用網絡或打開 MS DTC 端口:運行netsh firewall set allowedprogram %windir%/system32/msdtc.exe MSDTC enable命令就可以了
ASP.NET中的自動化事務
通過在ASP.NET頁面中添加Transaction屬性,可使得ASP.NET能夠在系統中支持自動事務。利用Transaction屬性,開發人員能夠指示頁面參與現有事務,開始新事務,或者不參與事務。下表列舉了ASP.NET中可用的Transaction屬性值。
通過在代碼中的Page指令中設置Transaction屬性能夠定義頁面支持的事務級別。例如,插入以下指令能夠保證頁面活動總是在事務范圍中執行:
<%@ Page Transaction="Required" %>
如果省略Transaction屬性,頁面則禁用事務。使用System.EnterpriseServices.ContextUtil類的靜態方法在ASP.NET頁面中提交或者放棄事務。這些靜態方法是SetComplete()和SetAbort()(它們分別對應Page事件CommitTransaction()和AbortTransaction())。以下代碼列舉了頁面實現框架,該頁面將Page指令的Transaction屬性設置為Required,同時在CommitTransaction()和AbortTransaction()事件中,編寫處理事務結果所需的代碼。
void Page_Load(object sender, System.EventArgs e) { AbortTransaction += new System.EventHandler(AbortTransactionEvent); CommitTransaction += new System.EventHandler(CommitTransactionEvent); try { /* 在這里放置事務性代碼 */ ContextUtil.SetComplete(); } catch (Exception) { ContextUtil.SetAbort(); } } void AbortTransactionEvent(object sender, System.EventArgs e) { /*用於回滾行為的代碼*/ } void CommitTransactionEvent(object sender, System.EventArgs e) { /*用於提交行為的代碼*/ }
}
4. 何時使用事務
雖然.NET 2.0對事務提供了很好的支持,但是沒有必要總是使用事務。使用事務的第一條規則是,在能夠使用事務的時候都應該使用事務,但是不要使用過度。原因在於,每次使用事務,都會占用一定的開銷。另外,事務可能會鎖定一些表的行。還有一條規則是,只有當操作需要的時候才使用事務。例如,如果只是從數據庫中查詢一些記錄,或者執行單個查詢,在大部分時候都不需要使用顯式事務。
開發人員應該在頭腦中始終保持一個概念,就是用於修改多個不同表數據的冗長事務會嚴重妨礙系統中的所有其他用戶。這很可能導致一些性能問題。當實現一個事務時,遵循下面的實踐經驗能夠達到可接受的結果:(1)避免使用在事務中的SELECT返回數據,除非語句依賴於返回數據;(2)如果使用SELECT語句,只選擇需要的行,這樣不會鎖定過多的資源,而盡可能的提高性能;(3)盡量將事務全部寫在T-SQL或者API中;(4)避免事務與多重獨立的批處理工作結合,應該將這些批處理放置在單獨的事務中;(5)盡可能避免大量更新。
另外,必須注意的一點就是事務的默認行為。在默認情況下,如果沒有顯式的提交事務,則事務會回滾。雖然默認行為允許事務的回滾,但是顯式回滾方法總是一個良好的編程習慣。這不僅僅只是釋放鎖定數據,也將使得代碼更容易讀並且更少錯誤。