事務(wù)是一組組合成邏輯工作單元的數(shù)據(jù)庫操作,在系統(tǒng)執(zhí)行過程中可能會(huì)出錯(cuò),但事務(wù)將控制和維護(hù)每個(gè)數(shù)據(jù)庫的一致性和完整性。事務(wù)處理的主要特征是,任務(wù)要么全部完成,要么都不完成。在寫入一些記錄時(shí),要么寫入所有記錄,要么什么都不寫入。如果在寫入一個(gè)記錄時(shí)出現(xiàn)了一個(gè)失敗,那么在事務(wù)處理中已寫入的其他數(shù)據(jù)就會(huì)回滾。事務(wù)可能由很多單個(gè)任務(wù)構(gòu)成。 簡單事務(wù)的一個(gè)常見例子:把錢從A賬戶轉(zhuǎn)到B賬戶,這涉及兩項(xiàng)任務(wù),即從A賬戶把錢取出來;把錢存入B賬戶。兩項(xiàng)任務(wù)要么同時(shí)成功,要么一起失敗,給予回滾,以便保持賬戶的狀態(tài)和原來相同。否則,在執(zhí)行某一個(gè)操作的時(shí)候可能會(huì)因?yàn)橥k?、網(wǎng)絡(luò)中斷等原因而出現(xiàn)故障,所以有可能更新了一個(gè)表中的行,但沒有更新相關(guān)表中的行。如果數(shù)據(jù)庫支持事務(wù),則可以將數(shù)據(jù)庫操作組成一個(gè)事務(wù),以防止因這些事件而使數(shù)據(jù)庫出現(xiàn)不一致。 事務(wù)的ACID屬性如下。 l 原子性(Atomicity):事務(wù)的所有操作是原子工作單元;對(duì)于其數(shù)據(jù)修改,要么全都執(zhí)行,要么全都不執(zhí)行。原子性消除了系統(tǒng)處理操作子集的可能性。 l 一致性(Consistency):數(shù)據(jù)從一種正確狀態(tài)轉(zhuǎn)換到另一種正確狀態(tài)。事務(wù)在完成時(shí),必須使所有的數(shù)據(jù)都保持一致。在相關(guān)數(shù)據(jù)庫中,所有規(guī)則都必須應(yīng)用于事務(wù)的修改,以保持所有數(shù)據(jù)的完整性。當(dāng)事務(wù)結(jié)束時(shí),所有的內(nèi)部數(shù)據(jù)結(jié)構(gòu)都必須是正確的。在存款取款的例子中,邏輯規(guī)則是,錢是不能憑空產(chǎn)生或銷毀的,對(duì)于每個(gè)(收支)條目必須有一個(gè)相應(yīng)的抵衡條目產(chǎn)生,以保證賬戶是平的。 l 隔離性(Isolation):由并發(fā)事務(wù)所作的修改必須與任何其他并發(fā)事務(wù)所作的修改隔離。查看數(shù)據(jù)時(shí)數(shù)據(jù)所處的狀態(tài),要么是事務(wù)修改它之前的狀態(tài),要么是事務(wù)修改它之后的狀態(tài)。簡單的理解就是,防止多個(gè)并發(fā)更新彼此干擾。事務(wù)在操作數(shù)據(jù)時(shí)與其他事務(wù)操作隔離。隔離性一般是通過加鎖的機(jī)制來實(shí)現(xiàn)的。 l 持久性(Durability):事務(wù)完成之后,它對(duì)于系統(tǒng)的影響是永久性的。已提交的更改即使在發(fā)生故障時(shí)也依然存在。 對(duì)于事務(wù)的開發(fā),.NET平臺(tái)也為我們提供了幾種非常簡單方便的事務(wù)機(jī)制。無論是在功能上還是性能上都提供了優(yōu)秀的企業(yè)級(jí)事務(wù)支持。 .NET開發(fā)者可以使用以下5種事務(wù)機(jī)制: l SQL和存儲(chǔ)過程級(jí)別的事務(wù)。 l ADO.NET級(jí)別的事務(wù)。 l ASP.NET頁面級(jí)別的事務(wù)。 l 企業(yè)級(jí)服務(wù)COM+事務(wù)。 l System.Transactions 事務(wù)處理。 這5種事務(wù)機(jī)制有著各自的優(yōu)勢(shì)和劣勢(shì),分別表現(xiàn)在性能、代碼數(shù)量和部署設(shè)置等方面。開發(fā)人員可以根據(jù)項(xiàng)目的實(shí)際情況選擇相應(yīng)的事務(wù)機(jī)制。下面就開始分別說明日常開發(fā)中5種事務(wù)的具體使用。
一、sql和存儲(chǔ)過程級(jí)別事務(wù)
數(shù)據(jù)庫事務(wù)是其他事務(wù)模型的基礎(chǔ),當(dāng)一個(gè)事務(wù)創(chuàng)建時(shí)不同數(shù)據(jù)庫系統(tǒng)都有自己的規(guī)則。SQL Server默認(rèn)在自動(dòng)提交的模式下工作,每個(gè)語句執(zhí)行完后都會(huì)立即提交;與此對(duì)照的是Oracle需要你包含一個(gè)提交語句。但是當(dāng)一個(gè)語句通過OLE DB執(zhí)行時(shí),它執(zhí)行完后一個(gè)提交動(dòng)作會(huì)被附加上去。例如: DECLARE @TranName VARCHAR(20); SELECT @TranName = 'MyTransaction';
BEGIN TRANSACTION @TranName; GO USE AdventureWorks; GO DELETE FROM AdventureWorks.HumanResources.JobCandidate WHERE JobCandidateID = 13;
COMMIT TRANSACTION MyTransaction; GO 或者: CREATE PROCEDURE Tran1 as begin tran set xact_abort on Insert Into P_Category(CategoryId,Name)values('1','test1') Insert Into P_Category(CategoryId,Name)values('2','test2') commit tran GO set xact_abort on表示遇到錯(cuò)誤立即回滾。 當(dāng)然你也可以這么寫: CREATE PROCEDURE tran1 as begin tran Insert Into P_Category(CategoryId,Name)values('1','test1') if(@@error<>0) rollback tran else begin Insert Into P_Category(CategoryId,Name)values('2','test2') if(@@error<>0) rollback tran else commit tran end GO 數(shù)據(jù)庫級(jí)別事務(wù)有它的優(yōu)勢(shì)和限制。 優(yōu)勢(shì): l 所有的事務(wù)邏輯包含在一個(gè)單獨(dú)的調(diào)用中。 l 擁有運(yùn)行一個(gè)事務(wù)的最佳性能。 l 獨(dú)立于應(yīng)用程序。 限制: l 事務(wù)上下文僅存在于數(shù)據(jù)庫調(diào)用中。 l 數(shù)據(jù)庫代碼與數(shù)據(jù)庫系統(tǒng)有關(guān)。
二、ado.net級(jí)別的事務(wù)
現(xiàn)在我們對(duì)事務(wù)的概念和原理都有所了解了,并且作為已經(jīng)有一些基礎(chǔ)的C#開發(fā)者,我們已經(jīng)熟知編寫數(shù)據(jù)庫交互程序的一些要點(diǎn),即: (1)使用SqlConnection類的對(duì)象的Open()方法建立與數(shù)據(jù)庫服務(wù)器的連接。 (2)然后將該連接賦給SqlCommand對(duì)象的Connection屬性。 (3)將欲執(zhí)行的SQL語句賦給SqlCommand的CommandText屬性。 (4)通過SqlCommand對(duì)象進(jìn)行數(shù)據(jù)庫操作。 創(chuàng)建一個(gè)ADO.NET事務(wù)是很簡單的,需要定義一個(gè)SqlTransaction類型的對(duì)象。SqlConnection 和OleDbConnection對(duì)象都有一個(gè) BeginTransaction 方法,它可以返回 SqlTransaction 或者OleDbTransaction 對(duì)象。然后賦給SqlCommand對(duì)象的Transcation屬性,即實(shí)現(xiàn)了二者的關(guān)聯(lián)。為了使事務(wù)處理可以成功完成,必須調(diào)用SqlTransaction對(duì)象的Commit()方法。如果有錯(cuò)誤,則必須調(diào)用Rollback()方法撤銷所有的操作。
(示例位置:光盤\code\ch05\04\Web\WebForm2) string conString = "data source=127.0.0.1;database=codematic;user id=sa; password="; SqlConnection myConnection = new SqlConnection(conString); myConnection.Open();
//啟動(dòng)一個(gè)事務(wù) SqlTransaction myTrans = myConnection.BeginTransaction();
//為事務(wù)創(chuàng)建一個(gè)命令 SqlCommand myCommand = new SqlCommand(); myCommand.Connection = myConnection; myCommand.Transaction = myTrans; try { myCommand.CommandText = "update P_Product set Name='電腦2' where Id=52"; myCommand.ExecuteNonQuery(); myCommand.CommandText = "update P_Product set Name='電腦3' where Id=53"; myCommand.ExecuteNonQuery(); myTrans.Commit();//提交 Response.Write("兩條數(shù)據(jù)更新成功"); } catch (Exception ex) { myTrans.Rollback();//遇到錯(cuò)誤,回滾 Response.Write(ex.ToString()); } finally { myConnection.Close(); } ADO.NET事務(wù)的優(yōu)勢(shì)和限制如下。 優(yōu)勢(shì): l 簡單。 l 和數(shù)據(jù)庫事務(wù)差不多快。 l 事務(wù)可以跨越多個(gè)數(shù)據(jù)庫訪問。 l 獨(dú)立于數(shù)據(jù)庫,不同數(shù)據(jù)庫的專有代碼被隱藏了。 限制:事務(wù)執(zhí)行在數(shù)據(jù)庫連接層上,所以需要在執(zhí)行事務(wù)的過程中手動(dòng)地維護(hù)一個(gè)連接。
三、asp.net 頁面級(jí)別事務(wù)處理 ASP.NET事務(wù)可以說是在.NET平臺(tái)上事務(wù)實(shí)現(xiàn)方式最簡單的一種,你僅僅需要一行代碼即可。在aspx的頁面聲明中加一個(gè)額外的屬性,即事務(wù)屬性Transaction="Required",它有如下的值:Disabled(默認(rèn))、NotSupported、Supported、Required和RequiresNew,這些設(shè)置和COM+及企業(yè)級(jí)服務(wù)中的設(shè)置一樣,典型的一個(gè)例子是如果你想在頁面上下文中運(yùn)行事務(wù),那么要將其設(shè)置為Required。如果頁面中包含有用戶控件,那么這些控件也會(huì)包含到事務(wù)中,事務(wù)會(huì)存在于頁面的每個(gè)地方。
代碼示例: 頁面聲明Transaction="Required": <%@ Page Transaction="Required" Language="C#" AutoEventWireup="true" CodeBehind="WebForm3.aspx.cs" Inherits="WebApplication4.WebForm3" %> 頁面引用:using System.EnterpriseServices;。 然后,數(shù)據(jù)操作代碼: protected void Button1_Click(object sender, EventArgs e) { try { Work1(); Work2(); ContextUtil.SetComplete(); //提交事務(wù) } catch (System.Exception except) { ContextUtil.SetAbort(); //撤銷事務(wù) Response.Write(except.Message); } } private void Work1() { string conString = "data source=127.0.0.1;database=codematic;user id=sa; password="; SqlConnection myConnection = new SqlConnection(conString); string strSql = "Insert Into P_Category(CategoryId,Name)values('1', 'test1')"; SqlCommand myCommand = new SqlCommand(strSql, myConnection); myConnection.Open(); int rows = myCommand.ExecuteNonQuery(); myConnection.Close(); } private void Work2() { string conString = "data source=127.0.0.1;database=codematic;user id=sa; password="; SqlConnection myConnection = new SqlConnection(conString); string strSql = "Insert Into P_Category(CategoryId,Name)values('2', 'test2')"; SqlCommand myCommand = new SqlCommand(strSql, myConnection); myConnection.Open(); int rows = myCommand.ExecuteNonQuery(); myConnection.Close(); } ContextUtil是用于獲取 COM+ 上下文信息的首選類。由于此類的成員全部為static,因此在使用其成員之前不需要對(duì)此類進(jìn)行實(shí)例化。 ASP.NET頁面事務(wù)的優(yōu)勢(shì)和限制如下。 l 優(yōu)勢(shì):實(shí)現(xiàn)簡單,不需要額外的編碼。 l限制:頁面的所有代碼都是同一個(gè)事務(wù),這樣的事務(wù)可能會(huì)很大,而也許我們需要的是分開的、小的事務(wù)實(shí)現(xiàn)在Web層。
四、企業(yè)級(jí)事務(wù)處理COM+
NET Framework 依靠 MTS/COM+ 服務(wù)來支持自動(dòng)事務(wù)處理。COM+ 使用 Microsoft Distributed Transaction Coordinator(DTC)作為事務(wù)管理器和事務(wù)協(xié)調(diào)器在分布式環(huán)境中運(yùn)行事務(wù)。這樣可使 .NET 應(yīng)用程序運(yùn)行跨多個(gè)資源結(jié)合不同操作(例如將定單插入SQL Server 數(shù)據(jù)庫、將消息寫入 Microsoft 消息隊(duì)列(MSMQ)隊(duì)列,以及從 Oracle 數(shù)據(jù)庫檢索數(shù)據(jù))的事務(wù)。 要實(shí)現(xiàn)COM+事務(wù)處理的類則必須繼承System.EnterpriseServices.ServicedComponent,這些類需要是公共的,并且需要提供一個(gè)公共的默認(rèn)的構(gòu)造器。其實(shí)Web Service就是繼承ServicedComponent,所以Web Service也支持COM+事務(wù)。要在類定義之前加屬性[Transaction(TransactionOption.Required)]。類里面的每個(gè)方法都會(huì)運(yùn)行在一個(gè)事務(wù)中。 定義一個(gè)COM+事務(wù)處理的類: 首先引用:using System.EnterpriseServices;。 然后,繼承ServicedComponent。 [Transaction(TransactionOption.Required)] public class OrderData : ServicedComponent { } TransactionOption枚舉類型支持5個(gè)值:Disabled、NotSupported、Required、RequiresNew和Supported,如表5-3所示。 表5-3 TransactionOption枚舉類型支持5個(gè)值
一般來說COM+中的組件需要Required 或Supported。當(dāng)組件用于記錄或查賬時(shí)RequiresNew 很有用,因?yàn)榻M件應(yīng)該與活動(dòng)中其他事務(wù)處理的提交或回滾隔離開來。 派生類可以重載基類的任意屬性。如OrderData選用Required,派生類仍然可以重載并指定RequiresNew或其他值。 COM+事務(wù)有手動(dòng)處理和自動(dòng)處理兩種方式,自動(dòng)處理就是在所需要自動(dòng)處理的方法前加上[AutoComplete],根據(jù)方法的正?;驋伋霎惓Q定提交或回滾。手動(dòng)處理就是調(diào)用ContextUtil類中的EnableCommit、SetComplete和SetAbort方法。 實(shí)現(xiàn)步驟如下。 1.給程序添加強(qiáng)名1)創(chuàng)建一對(duì)密鑰用來創(chuàng)建密鑰的工具是稱為sn.exe的共享工具。通常通過命令提示運(yùn)行它,該工具可執(zhí)行各種任務(wù)以生成并提取密鑰。我們需要用以下方式來運(yùn)行sn.exe。 sn –k c:\key.snk 其中key.snk 代表將保存密鑰的文件的名稱。它的名稱可以是任意的,不過習(xí)慣上帶有.snk后綴名。 2)簽名這個(gè)文件必須在AssemblyKeyFile屬性中引用,簽名通常是在編譯時(shí)進(jìn)行的。簽名時(shí),用戶可利用C#屬性通知編譯器應(yīng)該使用正確的密鑰文件對(duì)DLL進(jìn)行簽名。要做到這一點(diǎn)用戶需要打開工程中的AssemblyInfo.cs文件并進(jìn)行修改。 [assembly:AssemblyKeyFile(“..\\..\\key.snk”)]
2.手動(dòng)事務(wù)處理創(chuàng)建一個(gè)項(xiàng)目用以實(shí)現(xiàn)事務(wù)處理的業(yè)務(wù)類ClassTran。
using System; using System.Data.SqlClient; using System.EnterpriseServices; //企業(yè)級(jí)服務(wù)COM+事務(wù) namespace ClassTran { [Transaction(TransactionOption.Required)] public class OrderData1 : ServicedComponent { //手動(dòng)事務(wù) public string WorkTran() { try { ContextUtil.EnableCommit(); Work1(); Work2(); ContextUtil.SetComplete(); return "成功!"; } catch (Exception ex) { ContextUtil.SetAbort(); return "失敗!"; } } private void Work1() { string conString = "data source=127.0.0.1;database=codematic; user id=sa;password="; SqlConnection myConnection = new SqlConnection(conString); string strSql = "Insert Into P_Category(CategoryId,Name) values('1','test1')"; SqlCommand myCommand = new SqlCommand(strSql, myConnection); myConnection.Open(); int rows = myCommand.ExecuteNonQuery(); myConnection.Close(); } private void Work2() { string conString = "data source=127.0.0.1;database=codematic; user id=sa;password="; SqlConnection myConnection = new SqlConnection(conString); string strSql = "Insert Into P_Category(CategoryId,Name) values('2','test2')"; SqlCommand myCommand = new SqlCommand(strSql, myConnection); myConnection.Open(); int rows = myCommand.ExecuteNonQuery(); myConnection.Close(); } } } 3.自動(dòng)事務(wù)處理
(示例位置:光盤\code\ch05\04\ClassTran\OrderData2) using System; using System.Data.SqlClient; using System.EnterpriseServices;//企業(yè)級(jí)服務(wù)COM+事務(wù) namespace ClassTran { [Transaction(TransactionOption.Required)] public class OrderData2 : ServicedComponent { //自動(dòng)事務(wù) [AutoComplete(true)] public string WorkTran() { string msg = ""; string conString = "data source=127.0.0.1;database=codematic; user id=sa;password="; SqlConnection myConnection = new SqlConnection(conString); myConnection.Open(); SqlCommand myCommand = new SqlCommand(); myCommand.Connection = myConnection; try { myCommand.CommandText = "update P_Product set Name='電腦2' where Id=52"; myCommand.ExecuteNonQuery(); myCommand.CommandText = "update P_Product set Name='電腦3' where Id=53"; myCommand.ExecuteNonQuery(); msg ="成功!"; } catch (Exception ex) { msg = "失敗:"+ex.Message; } finally { myConnection.Close(); } return msg; } } } 4.事務(wù)方法調(diào)用
protected void Button1_Click(object sender, EventArgs e) { ClassTran.OrderData1 od1 = new ClassTran.OrderData1(); od1.WorkTran(); } protected void Button2_Click(object sender, EventArgs e) { ClassTran.OrderData2 od2 = new ClassTran.OrderData2(); od2.WorkTran(); } 在需要事務(wù)跨 MSMQ 和其他可識(shí)別事務(wù)的資源(例如SQL Server 數(shù)據(jù)庫)運(yùn)行的系統(tǒng)中,只能使用 DTC 或 COM+ 事務(wù),除此之外沒有其他選擇。DTC 協(xié)調(diào)參與分布式事務(wù)的所有資源管理器,也管理與事務(wù)相關(guān)的操作。 企業(yè)級(jí)服務(wù)COM+事務(wù)的前提及優(yōu)缺點(diǎn)如下。 前提: l 需要強(qiáng)名字。 l 使用事務(wù)的對(duì)象需要繼承ServicedComponent。 優(yōu)勢(shì): l 執(zhí)行分布式事務(wù),多個(gè)對(duì)象可以輕松地運(yùn)行在同一個(gè)事務(wù)處理中,事務(wù)處理還可以自動(dòng)登記。 l 獲得COM+服務(wù),諸如對(duì)象構(gòu)建和對(duì)象池等。 缺點(diǎn): l 由于存在 DTC 和 COM 互操作性開銷,導(dǎo)致性能降低。 l COM+ 1.0要求每個(gè)事務(wù)的隔離級(jí)別都設(shè)置為Serializable。 l 使用Enterprise Services的事務(wù)總是線程安全的, 也就是說你無法讓多個(gè)線程參與到同一個(gè)事務(wù)中。
五、System.Transaction
在 .NET Framework 2.0中增加了System.Transactions,這是一種新的命名空間,完全專注于控制事務(wù)性行為。引入了執(zhí)行事務(wù)性工作的更簡單方法及一些新的性能優(yōu)化。System.Transactions提供了一個(gè)“輕量級(jí)”的、易于使用的Transaction框架。 在上節(jié)中,要實(shí)現(xiàn)Transaction需要利用EnterpriseServices,讓組件從ServiceComponent繼承下來。而通過System.Transactions,則只要簡單的幾行代碼,不需要繼承,不需要Attribute標(biāo)記。用戶根本不需要考慮是簡單事務(wù)還是分布式事務(wù)。新模型會(huì)自動(dòng)根據(jù)事務(wù)中涉及的對(duì)象資源判斷使用何種事務(wù)管理器。簡而言之,對(duì)于任何的事務(wù),用戶只要使用同一種方法進(jìn)行處理即可。 下面介紹System.Transactions的幾種用法。 首先要引用:using System.Transactions;。 其次,將事務(wù)操作代碼放在TransactionScope中執(zhí)行。如: using (TransactionScope ts = new TransactionScope()) { //事務(wù)操作代碼 ts.Complete(); }
(示例位置:光盤\code\ch05\04\ClassTran\OrderData3) using (TransactionScope ts = new TransactionScope())//使整個(gè)代碼塊成為事務(wù)性代碼 { #region 在這里編寫需要具備Transaction的代碼 string msg = ""; string conString = "data source=127.0.0.1;database=codematic;user id=sa; password="; SqlConnection myConnection = new SqlConnection(conString); myConnection.Open(); SqlCommand myCommand = new SqlCommand(); myCommand.Connection = myConnection; try { myCommand.CommandText = "update P_Product set Name='電腦2' where Id=52"; myCommand.ExecuteNonQuery(); myCommand.CommandText = "update P_Product set Name='電腦3' where Id=53"; myCommand.ExecuteNonQuery(); msg = "成功!"; } catch (Exception ex) { msg = "失敗:" + ex.Message; } finally { myConnection.Close(); } #endregion ts.Complete(); return msg; } 上面的代碼演示了在一個(gè)Transaction Scope里面打開一個(gè)數(shù)據(jù)庫連接的過程。這個(gè)數(shù)據(jù)庫連接由于處在一個(gè)Transaction Scope里面,所以會(huì)自動(dòng)獲得Transaction的能力。如果這里數(shù)據(jù)庫連接的是SQL Server 2005,那么這個(gè)Transaction將不會(huì)激活一個(gè)MSDTC管理的分布式事務(wù),而是會(huì)由.NET創(chuàng)建一個(gè)Local Transaction,性能非常高。但是如果是SQL Server 2000,則會(huì)自動(dòng)激活一個(gè)分布式事務(wù),在性能上會(huì)受一定的損失。 再看下面的例子: void MethodMoreConn() { using (TransactionScope ts = new TransactionScope()) { using (SqlConnection conn = new SqlConnection(conString1)) { conn.Open(); using (SqlConnection conn2 = new SqlConnection(conString2)) { conn2.Open(); } } ts.Complete(); } } 這個(gè)例子更加充分地說明了Transaction Scope的強(qiáng)大,兩個(gè)數(shù)據(jù)庫連接!雖然上面的conn和conn2是兩個(gè)不同的連接對(duì)象,可能分別連接到不同的數(shù)據(jù)庫,但是由于它們處在一個(gè)TransactionScope中,它們就具備了“聯(lián)動(dòng)”的Transaction能力。在這里,將自動(dòng)激活一個(gè)MSDTC管理的分布式事務(wù)(可以通過打開【管理工具】里面的組件服務(wù),來查看當(dāng)前的分布式事務(wù)列表)。 1.在分布式事務(wù)中登記ADO.NET 2.0 中的新增功能支持使用 EnlistTransaction 方法在分布式事務(wù)中登記。由于 EnlistTransaction 在 Transaction 實(shí)例中登記連接,因此,該方法利用 System.Transactions 命名空間中的可用功能來管理分布式事務(wù),從而比使用 System.EnterpriseServices. ITransaction 對(duì)象的 EnlistDistributedTransaction 更可取。此外,其語義也稍有不同:在一個(gè)事務(wù)中顯式登記了某個(gè)連接后,如果第一個(gè)事務(wù)尚未完成,則無法取消登記或在另一個(gè)事務(wù)中登記該連接。 void MethodEnlist() { CommittableTransaction tx = new CommittableTransaction(); using (SqlConnection conn = new SqlConnection(conString)) { conn.EnlistTransaction(tx); } tx.Commit(); } 2.實(shí)現(xiàn)嵌套事務(wù)范圍void RootMethod() { using (TransactionScope scope = new TransactionScope()) { //操作代碼 SonMethod();//子事務(wù)方法 scope.Complete(); } } void SonMethod() { using (TransactionScope scope = new TransactionScope()) { //操作代碼 scope.Complete(); } } 3.事務(wù)范圍附加選項(xiàng)如果你想要保留代碼部分執(zhí)行的操作,并且在操作失敗的情況下不希望中止環(huán)境事務(wù),則Suppress對(duì)你很有幫助。例如,在你想要執(zhí)行日志記錄或?qū)徍瞬僮鲿r(shí),不管你的環(huán)境事務(wù)是提交還是中止,上述值都很有用。該值允許你在事務(wù)范圍內(nèi)具有非事務(wù)性的代碼部分,如以下示例所示。 void MethodSuppress() { using (TransactionScope scope1 = new TransactionScope())//開始事務(wù) { try { //開始一個(gè)非事務(wù)范圍 using (TransactionScope scope2 = new TransactionScope( TransactionScopeOption.Suppress)) { //不受事務(wù)控制代碼 } //從這里開始又回歸事務(wù)處理 } catch { } } } 雖然.NET 2.0對(duì)事務(wù)提供了很好的支持,但是沒有必要總是使用事務(wù)。使用事務(wù)的第一條規(guī)則是,在能夠使用事務(wù)的時(shí)候都應(yīng)該使用事務(wù),但是不要使用過度。原因在于,每次使用事務(wù)都會(huì)占用一定的開銷。另外,事務(wù)可能會(huì)鎖定一些表的行。還有一條規(guī)則是,只有當(dāng)操作需要的時(shí)候才使用事務(wù)。例如,如果只是從數(shù)據(jù)庫中查詢一些記錄,或者執(zhí)行單個(gè)查詢,則在大部分時(shí)候都不需要使用顯式事務(wù)。 開發(fā)人員應(yīng)該在頭腦中始終保持一個(gè)概念,就是用于修改多個(gè)不同表數(shù)據(jù)的冗長事務(wù)會(huì)嚴(yán)重妨礙系統(tǒng)中的所有其他用戶。這很可能導(dǎo)致一些性能問題。當(dāng)實(shí)現(xiàn)一個(gè)事務(wù)時(shí),遵循下面的實(shí)踐經(jīng)驗(yàn)?zāi)軌蜻_(dá)到可接受的結(jié)果: l 避免使用在事務(wù)中的Select返回?cái)?shù)據(jù),除非語句依賴于返回?cái)?shù)據(jù)。 l 如果使用Select語句,則只選擇需要的行,這樣不會(huì)鎖定過多的資源,而盡可能地提高性能。 l 盡量將事務(wù)全部寫在T-SQL或者API中。 l 避免事務(wù)與多重獨(dú)立的批處理工作結(jié)合,應(yīng)該將這些批處理放置在單獨(dú)的事務(wù)中。 l 盡可能避免大量更新。 另外,必須注意的一點(diǎn)就是事務(wù)的默認(rèn)行為。在默認(rèn)情況下,如果沒有顯式地提交事務(wù),則事務(wù)會(huì)回滾。雖然默認(rèn)行為允許事務(wù)的回滾,但是顯式回滾方法總是一個(gè)良好的編程習(xí)慣。這不僅僅只是釋放鎖定數(shù)據(jù),也將使得代碼更容易讀取并且更少錯(cuò)誤。 .NET提供的事務(wù)功能很強(qiáng)大,具體的內(nèi)容遠(yuǎn)不止本文所講解的這樣簡單。本文只是起到一個(gè)拋磚引玉的功能。希望讀者能夠靈活恰當(dāng)?shù)厥褂檬聞?wù)功能,而不要過度使用事務(wù),否則可能會(huì)對(duì)性能起到消極的作用。
|
|