net中的4種事務總結

風靈使發表於2018-07-26

在一個MIS系統中,沒有用事務那就絕對是有問題的,要麼就只有一種情況:你的系統實在是太小了,業務業務邏輯有隻要一步執行就可以完成了。因此掌握事務處理的方法是很重要,進我的歸類在.net中大致有以下4種事務處理的方法。大家可以參考一下,根據實際選擇適當的事務處理。

1 SQL事務

sql事務是使用SQL server自身的事務:在儲存過程中直接使用Begin TranRollback TranCommit 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
        /// <summary>
        /// SQL事務:
        /// </summary>
        public void SQLTran()
        {
            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();
            cmd.CommandText = "SPTransaction";
            cmd.CommandType = CommandType.StoredProcedure;
            cmd.Connection = conn;
            conn.Open();
            SqlParameter[] paras= new SqlParameter[]{
                                        new SqlParameter ("@UpdateID",SqlDbType.Int,32),
                                        new SqlParameter ("@UpdateValue",SqlDbType .NChar,50),
                                        new SqlParameter ("@InsertID",SqlDbType.Int ,32),
                                        new SqlParameter ("@InsertValue",SqlDbType.NChar ,50)};
            paras[0].Value = "2";
            paras[1].Value = "Update Value1";
            paras[2].Value = "6";
            paras[3].Value = "Insert Value1";
            foreach (SqlParameter para in paras )
            {
                cmd.Parameters.Add(para);
            }
            cmd.ExecuteNonQuery();   
        }

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)儘可能避免大量更新。
另外,必須注意的一點就是事務的預設行為。在預設情況下,如果沒有顯式的提交事務,則事務會回滾。雖然預設行為允許事務的回滾,但是顯式回滾方法總是一個良好的程式設計習慣。這不僅僅只是釋放鎖定資料,也將使得程式碼更容易讀並且更少錯誤。

相關文章