2013-03-15 72 views
12

對於我當前項目中的用法,我創建了一個允許我調用SQL Server異步的類。在事務中調用多個SQL Server存儲過程

我的代碼如下所示:

internal class CommandAndCallback<TCallback, TError> 
{ 
    public SqlCommand Sql { get; set; } 
    public TCallback Callback { get; set; } 
    public TError Error { get; set; } 
} 

class MyCodes:SingletonBase<MyCodes> 
{ 
    private static string _connString = @"Data Source=MyDB;Initial Catalog=ED;Integrated Security=True;Asynchronous Processing=true;Connection Timeout=0;Application Name=TEST"; 

    private MyCodes() { } 

    public void SetSystem(bool production) 
    { 
     _connString = 
      string.Format(@"Data Source=MyDB;Initial Catalog={0};Integrated Security=True;Asynchronous Processing=true;Connection Timeout=0;Application Name=TEST", production ? "ED" : "TEST_ED"); 
    } 

    public void Add(string newCode, Action<int> callback, Action<string> error) 
    { 
     var conn = new SqlConnection(_connString); 
     SqlCommand cmd = conn.CreateCommand(); 
     cmd.CommandTimeout = 0; 
     cmd.CommandType = CommandType.StoredProcedure; 
     cmd.CommandText = @"ADD_CODE"; 
     cmd.Parameters.Add("@NEW", SqlDbType.NVarChar).Value = newCode; 
     cmd.Parameters.Add("@NewId", SqlDbType.Int).Direction = ParameterDirection.Output; 

     try 
     { 
      cmd.Connection.Open(); 
     } 
     catch (Exception ex) 
     { 
      error(ex.ToString()); 
      return; 
     } 

     var ar = new CommandAndCallback<Action<int>, Action<string>> { Callback = callback, Error = error, Sql = cmd }; 
     cmd.BeginExecuteReader(Add_Handler, ar, CommandBehavior.CloseConnection); 
    } 

    private static void Add_Handler(IAsyncResult result) 
    { 
     var ar = (CommandAndCallback<Action<int>, Action<string>>)result.AsyncState; 
     if (result.IsCompleted) 
     { 
      try 
      { 
       ar.Sql.EndExecuteReader(result); 
       ar.Callback(Convert.ToInt32(ar.Sql.Parameters["@NewId"].Value)); 
      } 
      catch (Exception ex) 
      { 
       ar.Error(ex.Message); 
      } 
     } 
     else 
     { 
      ar.Error("Error executing SQL"); 
     } 
    } 

public void Update(int codeId, string newCode, Action callback, Action<string> error) 
    { 
     var conn = new SqlConnection(_connString); 
     SqlCommand cmd = conn.CreateCommand(); 
     cmd.CommandTimeout = 0; 
     cmd.CommandType = CommandType.StoredProcedure; 
     cmd.CommandText = @"UPDATE_CODE"; 
     cmd.Parameters.Add("@CODE_ID", SqlDbType.Int).Value = codeId; 
     cmd.Parameters.Add("@NEW", SqlDbType.NVarChar).Value = newCode; 

     try 
     { 
      cmd.Connection.Open(); 
     } 
     catch (Exception ex) 
     { 
      error(ex.ToString()); 
      return; 
     } 

     var ar = new CommandAndCallback<Action, Action<string>> { Callback = callback, Error = error, Sql = cmd }; 
     cmd.BeginExecuteReader(Update_Handler, ar, CommandBehavior.CloseConnection); 
    } 

    private static void Update_Handler(IAsyncResult result) 
    { 
     var ar = (CommandAndCallback<Action, Action<string>>)result.AsyncState; 
     if (result.IsCompleted) 
     { 
      try 
      { 
       ar.Sql.EndExecuteReader(result); 
       ar.Callback(); 
      } 
      catch (Exception ex) 
      { 
       ar.Error(ex.Message); 
      } 
     } 
     else 
     { 
      ar.Error("Error executing SQL"); 
     } 
    } 

} 

這可能看起來像太多的代碼,但它讓我叫它像這樣:

private void Add_Click(object sender, EventArgs e) 
{ 
    MyCodes.Instance.Add("Test",Success,Error) 
} 

private void Success(int newId) 
{ 
    MessageBox.Show(newId.ToString(), "Success", MessageBoxButtons.OK, MessageBoxIcon.Information); 
} 

private void Error(string error) 
{ 
    MessageBox.Show(error, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); 
} 

上面的代碼工作對我蠻好,我可以做每個呼叫異步。

我現在的問題是做多次調用交易 - 我想更新2個代碼並添加一個新的。

通常我會調用update,然後在成功處理程序中調用第二次更新,並在處理程序中進行第二次更新,我會調用add來返回新的id。

喜歡的東西:

-UPDATE CODE 
|-UPDATE CODE 
    |-ADD CODE (only this one return something) 

但我想呼籲所有的交易,因此,如果添加代碼將打破更新將回滾。

問:

是否有可能調用多個異步查詢的交易?

我可以將我的上述方法稱爲事務,還是必須創建單獨的方法來將我的過程作爲一個方法調用? (我想避免這一點,因爲它只是將相同的代碼從一種方法複製到另一種方法)

我想補充一點,我使用.NET 3.5所以等待和其他不錯的功能不是一個選項。

+0

不幸的是,要在一次交易中包裝所有的程序,您必須一個接一個地執行它們。否則,您將最終每次執行交易。 – LukeHennerley 2013-03-15 11:33:55

+0

LukeHennerly - 你能幫我建立一個將多個程序作爲一個調用的方法嗎?理想情況下,它需要代碼列表更新和代碼添加爲參數,當然它應該被稱爲異步如上 – Misiu 2013-03-15 11:44:45

回答

7

是的,這是可能的。只需在您的第一個電話之前致電SqlConnection.BeginTransaction,讓您將鏈接中的每個​​歸還SqlTransaction對象,並在末尾呼叫SqlTransaction.Commit()

+0

我會馬上檢查:) – Misiu 2013-03-15 11:52:54

+0

我試過這與刪除必須先刪除一些foreigen鍵(分離存儲過程),我沒有得到它運行,但與保存和更新它工作正常 – WiiMaxx 2014-06-26 08:09:16

16
string cnnString =WebConfigurationManager.ConnectionStrings["MyString"].ConnectionString; 
    SqlConnection cnn = new SqlConnection(cnnString); 
    SqlTransaction transaction; 

    cnn.Open(); 
    transaction = cnn.BeginTransaction(); 

    try 
    { 

     // Command Objects for the transaction 
     SqlCommand cmd1 = new SqlCommand("sproc1", cnn); 
     SqlCommand cmd2 = new SqlCommand("sproc2", cnn); 

     cmd1.CommandType = CommandType.StoredProcedure; 
     cmd2.CommandType = CommandType.StoredProcedure; 

     cmd1.Parameters.Add(new SqlParameter("@Param1", SqlDbType.NVarChar, 50)); 
     cmd1.Parameters["@Param1"].Value = paramValue1; 

     cmd1.Parameters.Add(new SqlParameter("@Param2", SqlDbType.NVarChar, 50)); 
     cmd1.Parameters["@Param2"].Value = paramValue2; 

     cmd2.Parameters.Add(new SqlParameter("@Param3", SqlDbType.NVarChar, 50)); 
     cmd2.Parameters["@Param3"].Value = paramValue3; 

     cmd2.Parameters.Add(new SqlParameter("@Param4", SqlDbType.NVarChar, 50)); 
     cmd2.Parameters["@Param4"].Value = paramValue4; 

     cmd1.ExecuteNonQuery(); 
     cmd2.ExecuteNonQuery(); 

     transaction.Commit(); 
    } 

    catch (SqlException sqlEx) 
    { 
     transaction.Rollback(); 
    } 

    finally 
    { 
     cnn.Close(); 
     cnn.Dispose(); 
    } 
+0

感謝您回答這樣的老問題:)在空閒時間我會檢查您的解決方案。現在我將所有東西都遷移到.NET 4.5,所以我將在創建我的數據庫訪問類時實現此功能。 – Misiu 2013-09-10 13:34:03

+0

還提到'SqlCommand.Transaction'這種方式'SqlCommand cmd1 = new SqlCommand(「sproc1」,cnn,transaction);'或者這種方式'cmd1.Transaction = transaction'以防萬一得到錯誤信息:_ExecuteNonQuery需要命令當分配給該命令的連接處於未決的本地事務中時有事務。該命令的Transaction屬性尚未初始化。採取從https://stackoverflow.com/a/10649035/1369235 – hims056 2017-06-20 13:28:32

相關問題