設計共用類別Transaction慎重使用

最近在設計一個較複雜的邏輯控制類別,因為內部較多SQL的存取所以為了方便起見,就在該類別所提供的Method當中包了Transcation,且在該Method的最後安插了一個Event(在尚未Commit之前)給使用者調用。

但這樣的設計上本身存在著一些陷阱,因為你無法預防使用者會如何使用你公開的Method,最近就遇到一個使用方式。 使用者在自己的Method內包了一層Transcation,在呼叫Component公開的Method,結果就發生了Timeout的事情,由這個經驗可以告訴我們,若再設計給其它第三者的API時,必須多考慮其它可能的使用方式。

下面的Sample大概舉例我所說的例子,請自行參考。
protected void Page_Load(object sender, EventArgs e)
{
    try
    {
        FirstMethods();
    }
    catch (Exception ex)
    {
        Response.Write(string.Format("Page_Load Exception :: {0}", ex.Message));
    }
}

private void FirstMethods()
{
    try
    {
        using (SqlConnection conn = new SqlConnection(_connString))
        {
            conn.Open();
            SqlTransaction trans = conn.BeginTransaction();

            using (SqlCommand cmd = conn.CreateCommand())
            {
                cmd.Transaction = trans;

                //新增第一個資料表的資料
                cmd.CommandText = "insert into testFirst (id,name) values(newid(),'');";
                cmd.ExecuteNonQuery();

                //在Transcation尚未結束後呼叫另外一個有包Transcation的Function
                SecondMethods();

                cmd.Transaction.Commit();
            }
        }
    }
    catch (Exception ex)
    {
        Response.Write(string.Format("FirstMethods Exception :: {0}", ex.Message));
    }
}

private void SecondMethods()
{
    try
    {
        using (SqlConnection conn = new SqlConnection(_connString))
        {
            conn.Open();
            SqlTransaction trans = conn.BeginTransaction();

            using (SqlCommand cmd = conn.CreateCommand())
            {
                cmd.Transaction = trans;

                //讀取前一個Methods新增資料的資料表
                cmd.CommandText = "select * from testFirst";
                cmd.ExecuteNonQuery();

                cmd.Transaction.Commit();
            }
        }
    }
    catch (Exception ex)
    {
        Response.Write(string.Format("SecondMethods Exception :: {0}", ex.Message));
    }
}

留言