DataSource Control 與 Transaction的課題

摘要: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的參考,於ButtonClick事件中鍵入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機制,這也是可以辦到的,只要運用UpdatingDeletingInserting等事件即可,見程式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(
ConfigurationManager.ConnectionStrings["NorthwindConnectionString"].ConnectionString);

        _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來處理這種情況,會是較正確的選擇。