2013-03-15 76 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

相关问题