使用Dapper并行执行多个查询

4

我想使用Dapper和存储过程并行执行三个相似的SQL查询,以便在所有查询完成后获取三个相似的结果。

下面是我的代码:

public class SomeReport
{
    private static readonly string ConnectionString = ConfigurationManager.ConnectionStrings["SomeContext"].ToString();
    public ReportStatus ReportStatus { get; set; }
    public long NetworkServerTime { get; set; }
    public string ReportLastErrorMessage { get; set; }

    public RowSet[] FirstRowSet { get; set; }
    public RowSet[] SecondRowSet { get; set; }
    public RowSet[] ThirdRowSet { get; set; }

    public Report()
    {
        NetworkServerTime = 0;
        ReportStatus = ReportStatus.NotCreated;
    }


    public async Task GetReportDataAsync(ReportParameters parameters)
    {
        DynamicParameters requestParameters = new DynamicParameters();
        requestParameters.Add("@sinceDateFilter", parameters.SinceDate?.Date, DbType.DateTime);
        requestParameters.Add("@untilDateFilter", parameters.UntilDate?.Date, DbType.DateTime);
        requestParameters.Add("@countryId", parameters.CountryId, DbType.Int32);

        ReportLastErrorMessage = null;

        Task allTasks = null;
        var stopWatch = new Stopwatch();

        try
        {
            var firstTask = GetRows("[dbo].[GET_Report_FirstRowSet]", requestParameters);
            var secondTask =
                GetRows("[dbo].[GET_Report_SecondRowSet]", requestParameters);
            var thirdTask =
                GetRows("[dbo].[GET_Report_ThirdRowSet]", requestParameters);

            allTasks = Task.WhenAll(firstTask, secondTask, thirdTask);

            FirstRowSet = await firstTask;
            SecondRowSet = await secondTask;
            ThirdRowSet = await thirdTask;
        }
        catch (Exception ex)
        {
            ReportStatus = ReportStatus.Error;
            ReportLastErrorMessage = allTasks?.Exception?.InnerExceptions.Last().Message;
        }
        finally
        {
            if (ReportStatus != ReportStatus.Error)
            {
                ReportStatus = ReportStatus.Success;
                NetworkServerTime = stopWatch.ElapsedMilliseconds;
            }

        }
        stopWatch.Reset();
    }

    private async Task<RowSet[]> GetRows(string procName, DynamicParameters parameters)
    {
        using (var conn = new SqlConnection(ConnectionString))
        {
            RowSet[] rowsSet;

            try
            {
                var sqlString = string.Concat(procName, " @sinceDateFilter, @untilDateFilter, @countryId");
                var query = await conn.QueryAsync<RowSet>(sqlString, parameters, commandTimeout: 500);
                rowsSet = query.ToArray();
            }
            catch (SqlException sqlEx)
            {
                rowsSet = new RowSet[0];
                throw;
            }

            return rowsSet;
        }
    }
}

但是当我启动调试器和SQL Server Profiler时,我看到创建相应任务的查询是按顺序执行的。

我该如何使查询同时开始运行并并行运行?


4
您需要先等待await allTasks,否则您实际上并未使用Task.WhenAll - Rhumborl
在我看来,它似乎已经按照你想要的方式运行了(除了上面评论中的小问题)- 你认为它们实际上是顺序的,而不仅仅是因为它们是按照顺序启动而报告的吗?另外:SqlClient可能对这里的有效/无效有所看法。 - Marc Gravell
@Marc 如果我使用调试器和 SQL Profiler,我会发现在代码行 var firstTask = GetRows("[dbo].[GET_Report_FirstRowSet]", requestParameters); 上执行了第一个查询,而不是在代码行 allTasks = Task.WhenAll (firstTask, secondTask, thirdTask); 上执行。 - Andrey Dengin
嗨,为什么不使用多查询 - Mahyar Mottaghi Zadeh
2个回答

4
如果我使用调试器和SQL Profiler,我会发现在代码行var firstTask = GetRows("[dbo].[GET_Report_FirstRowSet]", requestParameters);时,Profiler中的第一个查询被执行,而不是当我在代码行allTasks = Task.WhenAll (firstTask, secondTask, thirdTask);时被执行。
这是正确和正常的。异步/等待的工作方式是,只要第一个未完成的await发生,控制就返回到调用堆栈,这在您的情况下是await conn.QueryAsync<RowSet>。但是,您仍然已经通过简单地调用异步方法来启动了操作。该操作不会处于某个挂起状态,等待您调用Task.WhenAll,因此我们希望它已经开始了。 Task.WhenAll仅聚合等待步骤-它没有使事情真正发生的作用。
所以:我怀疑一切都已经按预期工作,但仅仅是任务按您请求的顺序报告为“开始”。这正是我们所期望的。

-1

因为所有任务已经在声明时运行。当您从返回的GetRow方法中分配任务时,您已经让它开始了。然后使用Task.WhenAll创建一个新任务,该任务将在所有子任务完成时返回已完成。但您没有等待此任务。

因此,您可以使用await关键字确保执行完成时三个任务都已完成。

如果您可以将所有执行都包装在独立的任务中,那么您可以像这样并行调用它们。


var queries = new[] { 
    "[dbo].[GET_Report_FirstRowSet]", 
    "[dbo].[GET_Report_SecondRowSet]", 
    "[dbo].[GET_Report_ThirdRowSet]" 
};

var tasks = queries.Select(query => new Task(()=>{
    return GetRows(query, requestParameters);
})).ToArray();

Task.WaitAll(tasks);


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接