2013-03-15 5 views
12

मेरी वर्तमान प्रोजेक्ट में उपयोग के लिए मैंने एक कक्षा बनाई है जो मुझे SQL सर्वर async को कॉल करने की अनुमति देती है।लेनदेन में एकाधिक SQL सर्वर संग्रहीत प्रक्रियाओं को कॉल करें

मेरे कोड इस तरह दिखता है:

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); 
} 

कोड से ऊपर मेरे लिए बस ठीक काम करता है, मैं हर कॉल async करने में सक्षम हूँ।

समस्या जो मेरे पास अभी है, लेनदेन के रूप में कई कॉल करना है - मैं 2 कोड अपडेट करना और एक नया जोड़ना चाहता हूं।

आम तौर पर मैं अद्यतन कहूंगा, फिर सफलतापूर्वक हैंडलर कॉल दूसरे अपडेट में, और हैंडलर में दूसरे अपडेट में मैं जोड़ दूंगा जो नई आईडी वापस कर देगा।

कुछ की तरह:

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

लेकिन मैं, लेन-देन के रूप में उन के सभी कॉल करने के लिए, इसलिए यदि कोड जोड़ टूट जाएगा अद्यतन रोलबैक होगा चाहते हैं।

प्रश्न:

यह एक सौदे के रूप में कई async प्रश्नों कॉल करने के लिए संभव है?

क्या मैं अपने उपरोक्त तरीकों को लेनदेन के रूप में कॉल कर सकता हूं या क्या मुझे अपनी प्रक्रियाओं को कॉल करने के लिए अलग विधि बनाना चाहिए? (मैं इसे एक से बचाना चाहता हूं क्योंकि यह सिर्फ एक ही कोड को एक विधि से दूसरे में कॉपी कर रहा है)

मैं यह जोड़ना चाहता हूं कि मैं .NET 3.5 का उपयोग करता हूं, इसलिए प्रतीक्षा करें और अन्य अच्छी सुविधाएं एक विकल्प नहीं हैं।

+0

दुर्भाग्य से, एक लेनदेन में अपनी सभी प्रक्रियाओं को लपेटने के लिए आपको उन्हें एक दूसरे के बाद निष्पादित करना होगा। आप अन्यथा प्रति निष्पादन लेनदेन होने के अंत में समाप्त हो जाएगा। – LukeHennerley

+0

ल्यूकहेनरली - क्या आप मुझे ऐसी विधि बनाने में मदद कर सकते हैं जो एकाधिक प्रक्रियाओं को एक के रूप में कॉल करेगी? आदर्श रूप से यह पैरामीटर के रूप में जोड़ने के लिए कोड और कोड को जोड़ने के लिए कोडों की सूची लेगा और निश्चित रूप से इसे – Misiu

उत्तर

7

हां, यह संभव है। बस अपनी पहली कॉल से पहले SqlConnection.BeginTransaction पर कॉल करें, आपको श्रृंखला में प्रत्येक SqlCommand.Transaction पर लौटा SqlTransaction ऑब्जेक्ट असाइन करें और अंत में SqlTransaction.Commit() पर कॉल करें।

+0

के रूप में एसिंक कहा जाना चाहिए, मैं इसे तुरंत देखूंगा :) – Misiu

+0

मैंने इसे एक डिलीट के साथ करने की कोशिश की जिसे पहले कुछ फोरिजिन कुंजी को हटाना है (अलग संग्रहित प्रक्रियाएं) और मुझे इसे चलाने के लिए नहीं मिलता है, लेकिन इसे सहेजने और अपडेट करने के साथ यह ठीक काम करता है – WiiMaxx

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

+0

इस प्रकार 'SqlCommand.Transaction' का भी उल्लेख करें 'SqlCommand cmd1 = new SqlCommand ("sproc1", cnn, लेनदेन); या इस तरह से' cmd1.Transaction = लेनदेन 'यदि आपको त्रुटि संदेश मिलता है: _ExecuteNonQuery को कमांड की आवश्यकता होती है एक लेनदेन है जब आदेश को सौंपा गया कनेक्शन लंबित स्थानीय लेनदेन में है। आदेश की लेनदेन संपत्ति प्रारंभ नहीं की गई है। Https://stackoverflow.com/a/10649035/1369235 से लिया गया – hims056

संबंधित मुद्दे

 संबंधित मुद्दे