2016-02-13 34 views
5

複数のデータベースコールを処理する適切な方法を探していますが、これは同時に実行すると有益です。クエリは、ASP.NET MVCアプリケーションのDataTableにプログラムでアセンブルされたデータを使用して挿入またはマージするストアドプロシージャにのみ適用されます。非同期/並列データベースコールを正しく作成する方法

もちろん、私はasyncawaitに関するいくつかの情報を見てきましたが、それは私がする必要があるようですが、実装方法をはっきりと理解していません。いくつかの情報は、通話は依然として順次であり、他の通話が完了するのを待っているということです。それは意味がないようです。

最終的には、最長の手順を完了するのにかかる時間内にすべてのクエリを実行できるソリューションが必要です。私はすべてのクエリが影響を受けるレコードの数を返すようにしたいと思います。そのコードのすべてが、DataTableのためのデータを組み立て同じメソッド内で

// 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

は、あなたがそれを行うだろうかの例です:

ここで私は2つの操作をラップする2つのメソッドを作成しています、あなたは他の操作のために同じことを実行する必要があります。

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を)それを呼び出すために持っていることを意味します:別のオプションは、(実際には1つ、より良い)は、このような非同期のいずれかにメソッドを変換することです。

+0

これはすばらしく見えます。実際にはGUIアプリケーションですが、クエリが終了するまでGUIをロックすることが期待されます。それらが完了すると、それぞれのクエリから影響を受けるレコードの数を表示するビューを読み込みます。私はその情報が欲しい/必要であるので、呼び出しページを赤ちゃんに寝かせるか、完了するまでに1分を与えてから戻ってきます。私はちょうど1つの質問ごとに5分待ってポイントを見なかった、私は同じ5分間待つことができ、それらのすべてをすぐに完了することができます。ありがとう! – FlipperBizkut

+4

'MergeOneDataTableAsync'と' MergeTwoDataTableAsync'によって作成されたタスクで、 'Configure.Wwait(false)'なしで 'await'を使うと、実際に' Task.WaitAll'を呼び出してデッドロックが発生しました。 –

+0

デッドロックの問題について詳しく説明できますか?おそらく同様にそれを避ける方法を示していますか? – FlipperBizkut

関連する問題