摘要:DataSource Control 與 Transaction的課題
DataSource Control 與 Transaction的課題
文/黃忠成
  Transaction一向是資料庫應用程式必須正視的課題,當使用ADO.NET時,可以透過Connection物件來啟動Trasnsaction,但來到了SqlDataSource控件時,又該如何啟動Transaction呢?這個問題並不容易回答,SqlDataSource控件的設計概念於更新時是以單筆資料為基礎,也就是說每次執行更新前會自動建立一個Connection物件,更新完成後就會Connection物件關閉並釋放掉,那這是否意味著我們無法使用SqlDataSource控件來處理Transaction呢?那麼這樣一來,更新出貨單時扣減庫存的動作不是就無法放在一個Transaction中了嗎?會有這個疑問是當然的,但是若細想後,你會查覺到前面ADO.NET一節中曾經提過的TransactionScope機制,運用這個機制是否能達到需求呢?讓我們試試吧,先在Northwind資料庫的Customers資料表中添加一個NOTES欄位,型態為varchar(50)或nvarchar(50)後,再建立一個WebSite專案,組態SqlDataSource控件連結至此資料表,於網頁上放入一個Button控件,然後在WebSite專案上按右鍵,新增對System.Transaction的參考,於Button的Click事件中鍵入4-5-13的程式碼。
程式4-5-13
using System; using System.Data; using System.Configuration; using System.Collections; using System.Web; using System.Web.Security; using System.Web.UI; using System.Web.UI.WebControls; using System.Web.UI.WebControls.WebParts; using System.Web.UI.HtmlControls; using System.Transactions; public partial class UpdateWithTransaction : System.Web.UI.Page {     protected void Page_Load(object sender, EventArgs e)     {     }     protected void Button1_Click(object sender, EventArgs e)     {         using (TransactionScope scope = new TransactionScope())         {             DataView dv = (DataView)SqlDataSource1.Select(DataSourceSelectArguments.Empty);             foreach (DataRowView rv in dv)             {                 foreach (DataColumn col in rv.Row.Table.Columns)                     SqlDataSource1.UpdateParameters[col.ColumnName].DefaultValue =  rv[col.ColumnName].ToString();                 SqlDataSource1.UpdateParameters["NOTES"].DefaultValue = "TEST11111";                 SqlDataSource1.Update();             }             scope.Complete();         }     } }  | 
執行此網頁按下Button後,再到資料庫查詢Customers資料表,你會發現所有NOTES欄位都已經被改變了。
圖4-5-37
請下達SQL指令,再次將NOTES清空。
圖4-5-38
然後移除程式4-5-13中的scope.Complete函式的呼叫程式碼,再次執行後,你會發現,NOTES值並未被改變。
圖4-5-39
這意味著,使用SqlDataSource控件搭配TransactionScope後,便可於SqlDataSource控件中使用交易。當然!有些人可能不是很喜歡TransactionScope這種隱誨式交易的寫法(我是其中之一,況且TransactionScope升級成DTC的問題,會嚴重影響程式的效能),而比較喜歡使用舊有的Transaction機制,這也是可以辦到的,只要運用Updating、Deleting、Inserting等事件即可,見程式4-5-14。
程式4-5-14
using System; using System.Data; using System.Data.Common; using System.Data.SqlClient; using System.Configuration; using System.Collections; using System.Web; using System.Web.Security; using System.Web.UI; using System.Web.UI.WebControls; using System.Web.UI.WebControls.WebParts; using System.Web.UI.HtmlControls; public partial class UpdateWithTrasnaction2 : System.Web.UI.Page {     private SqlConnection _tempConn = null;     private SqlTransaction _trans = null;     protected void Page_Load(object sender, EventArgs e)     {     }     protected void Button1_Click(object sender, EventArgs e)     {         _tempConn = new SqlConnection(         _tempConn.Open();         _trans = _tempConn.BeginTransaction();         try         {             DataView dv = (DataView)SqlDataSource1.Select(DataSourceSelectArguments.Empty);             foreach (DataRowView rv in dv)             {                 foreach (DataColumn col in rv.Row.Table.Columns)                     SqlDataSource1.UpdateParameters[col.ColumnName].DefaultValue =  rv[col.ColumnName].ToString();                 SqlDataSource1.UpdateParameters["NOTES"].DefaultValue = "TEST11111";                 SqlDataSource1.Update();             }           //  _trans.Commit();             _trans.Rollback();         }         catch (Exception)         {             _trans.Rollback();         }                 _tempConn.Close();     }     private void ReplaceNullValues(DbCommand command)     {         int count = command.Parameters.Count;         foreach (DbParameter parameter in command.Parameters)         {             if(parameter.Value == null)                 parameter.Value = DBNull.Value;         }     }     protected void SqlDataSource1_Updating(object sender, SqlDataSourceCommandEventArgs e)     {         e.Command.Connection = _tempConn;         e.Command.Transaction = _trans;         ReplaceNullValues(e.Command);         if (e.Command.ExecuteNonQuery() > 1)             throw new Exception("The Command affected more then one rows.");         e.Cancel = true;     } }  | 
將_trans.Rollback改為_trans.Complete後,便可正確將資料寫入資料庫,反之則是取消交易。不過說實話,如非必要,使用ObjectDataSource+TableAdapter、或直接操作ADO.NET來處理這種情況,會是較正確的選擇。