简体   繁体   English

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

[英]How to properly make asynchronous / parallel database calls

I'm looking for the proper way to handle multiple database calls that would likely benefit from running simultaneously.我正在寻找处理多个数据库调用的正确方法,这些调用可能会从同时运行中受益。 The queries are just to stored procedures that are either doing inserts or merges using data that is programmatically assembled into DataTables in my ASP.NET MVC app.查询仅针对使用以编程方式组装到我的 ASP.NET MVC 应用程序中的 DataTables 中的数据进行插入或合并的存储过程。

Of course I have seen some information on async and await , and that appears to be what I would need to do, but I don't have a clear understanding of how to implement it.当然,我已经看到了一些关于asyncawait ,这似乎是我需要做的,但我对如何实现它没有清楚的了解。 Some information is saying that the calls would still be sequential, and that one would still be waiting on another to complete.一些信息说这些调用仍然是连续的,并且一个仍然在等待另一个调用完成。 That seems pointless.这似乎毫无意义。

Ultimately, I would like a solution that allows me to run all the queries in the time it takes for the longest procedure to complete.最终,我想要一个解决方案,它允许我在最长的过程完成所需的时间内运行所有查询。 I would like all the queries to return the number of records affected (as they do now) as well.我希望所有查询也返回受影响的记录数(就像现在一样)。

Here is what I have going on now (which is in no way parallel):这是我现在正在做的事情(这绝不是平行的):

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

All of that code is within the same method that assembles the data for the DataTables.所有这些代码都在为 DataTables 组装数据的同一方法中。 My limited understanding of async would lead me to believe that I would need to extract the previous code into its own method.我对async有限理解使我相信我需要将以前的代码提取到它自己的方法中。 I would then call that method and await the return.然后我会调用该方法并await返回。 However, I don't even know enough about it to begin.但是,我什至对它的了解还不够。

I have never done any asynchronous/parallel/multithreaded coding before.我以前从未做过任何异步/并行/多线程编码。 This situation just makes me feel like it is the perfect time to jump in. That said, I would like to learn the best way, instead of having to unlearn the wrong way.这种情况只是让我觉得现在是进入的最佳时机。也就是说,我想学习最好的方法,而不是必须忘记错误的方法。

Here is an example of how you would do it:以下是您将如何执行此操作的示例:

Here I am creating two methods to wrap two operations, you need to do the same for the other operations:这里我创建了两个方法来包装两个操作,您需要对其他操作执行相同的操作:

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

Notice that I am using the ExecuteNonQueryAsync method to execute the query.请注意,我正在使用ExecuteNonQueryAsync方法来执行查询。

And then your original method would look like this:然后你的原始方法看起来像这样:

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

Please note that I am keeping this method synchronous.请注意,我保持此方法同步。 Another option (actually a better one) is to convert the method into an asynchronous one like this:另一种选择(实际上更好)是将方法转换为异步方法,如下所示:

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

But this would mean that you have to invoke it asynchronously ( async all the way ).但这意味着您必须异步调用它(一直异步)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何从WCF客户端并行调用WCF服务 - How to make asynchronous calls from a WCF client to WCF Service in parallel 如何使用HtmlAgilityPack进行异步调用? - How to make asynchronous calls using HtmlAgilityPack? 如何通过Escape键正确取消并行异步IO Task? - How to properly cancel parallel asynchronous IO Task by Escape key? 如何进行并行数据库调用并绑定到模型 ASP.NET Core MVC - How to make parallel database calls and bind to model ASP.NET Core MVC 正确处理一组异步调用的正确方法 - Proper way to handle a group of asynchronous calls in parallel 如何使WCF REST方法与任务并行库完全异步? - How to make a WCF REST method entirely asynchronous with the Task Parallel Library? 如何使用数据库调用优化和加速异步方法 - How to optimize and speed up an asynchronous method with database calls 如何进行一系列异步调用,然后使用第一个线程返回的值? - How to make a series of asynchronous calls and then use the value returned by first thread? 如何在ViewModel和Model之间进行通信异步调用 - How to make communicate asynchronous calls between ViewModel and Model 如何在 C# 中并行调用 web api - How to make parallel a web api calls in C#
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM