2016-02-13 50 views
5

我正在尋找正確的方式來處理多個數據庫調用,這可能會從同時運行中受益。這些查詢只是存儲過程,它們要麼使用在ASP.NET MVC應用程序中以編程方式組裝到DataTable中的數據進行插入或合併。如何正確進行異步/並行數據庫調用

當然,我已經看到了一些關於asyncawait的信息,這似乎是我需要做的,但我對如何實現它沒有清晰的理解。有些信息是說,這些電話仍然是連續的,而且還有人會等待另一個電話的完成。這似乎毫無意義。

最終,我想要一個解決方案,允許我在完成最長過程所需的時間內運行所有查詢。我希望所有的查詢都能返回受影響的記錄數(現在也是這樣)。

這裏是我怎麼回事,現在(這是在平行沒辦法):

// Variable for number of records affected 
var recordedStatistics = new Dictionary<string, int>(); 

// Connect to the database and run the update procedure 
using (var dbc = new SqlConnection(db.Database.Connection.ConnectionString)) 
{ 
    dbc.Open(); 

    // Merge One procedure 
    using (SqlCommand cmd = new SqlCommand("MergeOneProcedure", dbc)) 
    { 
     // 5 minute timeout on the query 
     cmd.CommandTimeout = 300; 
     cmd.CommandType = CommandType.StoredProcedure; 
     cmd.Parameters.AddWithValue("@TVP", MergeOneDataTable); 

     // Execute procedure and record the number of affected rows 
     recordedStatistics.Add("mergeOne", cmd.ExecuteNonQuery()); 
    } 

    // Merge Two procedure 
    using (SqlCommand cmd = new SqlCommand("MergeTwoProcedure", dbc)) 
    { 
     // 5 minute timeout on the query 
     cmd.CommandTimeout = 300; 
     cmd.CommandType = CommandType.StoredProcedure; 
     cmd.Parameters.AddWithValue("@TVP", MergeTwoDataTable); 

     // Execute procedure and record the number of affected rows 
     recordedStatistics.Add("mergeTwo", cmd.ExecuteNonQuery()); 
    } 

    // Merge Three procedure 
    using (SqlCommand cmd = new SqlCommand("MergeThreeProcedure", dbc)) 
    { 
     // 5 minute timeout on the query 
     cmd.CommandTimeout = 300; 
     cmd.CommandType = CommandType.StoredProcedure; 
     cmd.Parameters.AddWithValue("@TVP", MergeThreeDataTable); 

     // Execute procedure and record the number of affected rows 
     recordedStatistics.Add("mergeThree", cmd.ExecuteNonQuery()); 
    } 

    // Merge Four procedure 
    using (SqlCommand cmd = new SqlCommand("MergeFourProcedure", dbc)) 
    { 
     // 5 minute timeout on the query 
     cmd.CommandTimeout = 300; 
     cmd.CommandType = CommandType.StoredProcedure; 
     cmd.Parameters.AddWithValue("@TVP", MergeFourDataTable); 

     // Execute procedure and record the number of affected rows 
     recordedStatistics.Add("mergeFour", cmd.ExecuteNonQuery()); 
    } 

    // Merge Five procedure 
    using (SqlCommand cmd = new SqlCommand("MergeFiveProcedure", dbc)) 
    { 
     // 5 minute timeout on the query 
     cmd.CommandTimeout = 300; 
     cmd.CommandType = CommandType.StoredProcedure; 
     cmd.Parameters.AddWithValue("@TVP", MergeFiveDataTable); 

     // Execute procedure and record the number of affected rows 
     recordedStatistics.Add("mergeFive", cmd.ExecuteNonQuery()); 
    } 

    dbc.Close(); 
} 

return recordedStatistics; 

所有的代碼是組裝的數據表中的數據相同的方法內。我對async的有限理解會讓我相信我需要將以前的代碼提取到自己的方法中。然後我會打電話給那個方法和await的回報。但是,我甚至不知道該如何開始。

我從來沒有做過任何異步/並行/多線程編碼。這種情況讓我覺得這是跳入其中的絕佳時機。也就是說,我想學習最好的方法,而不是忽視錯誤的方式。

回答

7

這裏是你將如何做到這一點的例子:

在這裏,我創建了兩個方法來包裝兩種操作,您需要爲其他操作做同樣的:

public async Task<int> MergeOneDataTableAsync() 
{ 
    // Merge One procedure 
    using (SqlCommand cmd = new SqlCommand("MergeOneProcedure", dbc)) 
    { 
     // 5 minute timeout on the query 
     cmd.CommandTimeout = 300; 
     cmd.CommandType = CommandType.StoredProcedure; 
     cmd.Parameters.AddWithValue("@TVP", MergeOneDataTable); 

     return await cmd.ExecuteNonQueryAsync().ConfigureAwait(false); 
    } 
} 


public async Task<int> MergeTwoDataTableAsync() 
{ 
    // Merge Two procedure 
    using (SqlCommand cmd = new SqlCommand("MergeTwoProcedure", dbc)) 
    { 
     // 5 minute timeout on the query 
     cmd.CommandTimeout = 300; 
     cmd.CommandType = CommandType.StoredProcedure; 
     cmd.Parameters.AddWithValue("@TVP", MergeTwoDataTable); 

     return await cmd.ExecuteNonQueryAsync().ConfigureAwait(false); 
    } 
} 

注意我正在使用ExecuteNonQueryAsync方法來執行查詢。

然後你原來的方法是這樣的:

using (var dbc = new SqlConnection(db.Database.Connection.ConnectionString)) 
{ 
    dbc.Open(); 

    Task<int> task1 = MergeOneDataTableAsync(); 
    Task<int> task2 = MergeTwoDataTableAsync(); 

    Task.WaitAll(new Task[]{task1,task2}); //synchronously wait 

    recordedStatistics.Add("mergeOne", task1.Result); 
    recordedStatistics.Add("mergeTwo", task2.Result); 
} 

請注意,我保持這種方法同步。另一種選擇(實際上是一個更好的)是將方法轉換成異步一個這樣的:

public async Task<Dictionary<string, int>> MyOriginalMethod() 
{ 
    //... 
    using (var dbc = new SqlConnection(db.Database.Connection.ConnectionString)) 
    { 
     dbc.Open(); 

     Task<int> task1 = MergeOneDataTableAsync(); 
     Task<int> task2 = MergeTwoDataTableAsync(); 

     int[] results = await Task.WhenAll(new Task<int>[]{task1,task2}); 

     recordedStatistics.Add("mergeOne", results[0]); 
     recordedStatistics.Add("mergeTwo", results[1]); 
    } 

    //... 
    return recordedStatistics; 
} 

但是,這將意味着你必須異步調用它(async all the way)。

+0

這看起來不錯。實際上,它是一個GUI應用程序,但我希望它鎖定GUI直到查詢完成。一旦完成,我就會從每個查詢中加載一個受影響記錄數量的視圖。我想要/需要這些信息,所以我要麼是在打電話的網頁上留言,要麼我在回答完一分鐘後再回來。我只是沒有看到每個查詢等待5分鐘的時間,一個接一個地等待5分鐘,然後我可以一次完成所有查詢。謝謝! – FlipperBizkut

+4

實際上,在MergeOneDataTableAsync和MergeTwoDataTableAsync創建的任務上調用'Task.WaitAll'時,實際上發生了死鎖,它們都使用'await'而沒有'ConfigureAwait(false)'。 –

+0

您能詳細解釋死鎖問題嗎?也許還有一種方法可以避免它呢? – FlipperBizkut

相關問題