如何正确地进行异步/并行数据库调用

我正在寻找处理多个数据库调用的正确方法,这些调用可能会同时运行。 查询仅针对使用在ASP.NET MVC应用程序中以编程方式组装到DataTable中的数据进行插入或合并的存储过程。

当然我已经看到了关于asyncawait一些信息,这似乎是我需要做的,但我对如何实现它没有清楚的理解。 一些信息表明这些调用仍然是顺序调用的,并且仍然会等待另一个调用完成。 这似乎毫无意义。

最后,我想要一个解决方案,允许我在完成最长程序所需的时间内运行所有查询。 我希望所有查询都能返回受影响的记录数(就像现在一样)。

这就是我现在要做的事情(这绝不是平行的):

 // Variable for number of records affected var recordedStatistics = new Dictionary(); // 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; 

所有这些代码都在组装DataTables数据的相同方法中。 我对async有限理解会让我相信我需要将以前的代码提取到自己的方法中。 然后我会调用该方法并await返回。 但是,我甚至不知道它开始了。

我之前从未做过任何异步/并行/multithreading编码。 这种情况让我觉得这是进入的最佳时机。也就是说,我想学习最好的方法,而不是忘记错误的方式。

以下是如何执行此操作的示例:

这里我创建两个方法来包装两个操作,你需要为其他操作做同样的事情:

 public async Task 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 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 task1 = MergeOneDataTableAsync(); Task task2 = MergeTwoDataTableAsync(); Task.WaitAll(new Task[]{task1,task2}); //synchronously wait recordedStatistics.Add("mergeOne", task1.Result); recordedStatistics.Add("mergeTwo", task2.Result); } 

请注意,我保持此方法同步。 另一个选项(实际上是更好的选项)是将方法转换为异步方法,如下所示:

 public async Task> MyOriginalMethod() { //... using (var dbc = new SqlConnection(db.Database.Connection.ConnectionString)) { dbc.Open(); Task task1 = MergeOneDataTableAsync(); Task task2 = MergeTwoDataTableAsync(); int[] results = await Task.WhenAll(new Task[]{task1,task2}); recordedStatistics.Add("mergeOne", results[0]); recordedStatistics.Add("mergeTwo", results[1]); } //... return recordedStatistics; } 

但这意味着你必须异步调用它( 异步一直 )。