使用 Dapper 在同一 "using" 语句中选择多个标量的正确方法是什么?

What is the proper way of selecting multiple scalars within the same "using" statement with Dapper?

我写了下面的方法,希望通过并行执行任务使方法运行更快:

public static async Task<ExampleData> GetExampleDataAsync()
    {       
        try
        {
            var data = new ExampleData();
            
            using (SqlConnection connection = new SqlConnection(_connectionString)
            {
                //Do them in parallel
                var Task1 = connection.ExecuteScalarAsync<int>
                    ($"SELECT COUNT(*) FROM dbo.ExampleTable)");

                var Task2 = connection.ExecuteScalarAsync<int>
                    ($"Select Count(*) from dbo.ExampleTable where(row1 is null)");

                var Task3 = connection.ExecuteScalarAsync<double>
                    ($"Select SUM(row2) from dbo.ExampleTable");

                await Task.WhenAll(Task1, Task2, Task3);

                data.prop1 = await Task1;
                data.prop2 = await Task2;
                data.prop3 = await Task3;
            }

            return data;
        }
        catch (Exception)
        {
            throw;
        }         
    }

不幸的是,我收到以下异常:

System.InvalidOperationException: 'BeginExecuteReader requires an open and available Connection. The connection's current state is connecting.'

我该如何正确实施?

您似乎需要将每个查询包装在单独的 SqlConnection 中。所以你的代码看起来像这样:

public async Task<int> FooMethod_1()
{
    using (SqlConnection connection = new SqlConnection(_connectionString)
    {           
        return await connection.ExecuteScalarAsync<int>
            ($"SELECT COUNT(*) FROM dbo.ExampleTable)");
    }
}

然后 await 这个方法:

var task1 = FooMethod_1();
// other tasks...
await Task.WhenAll(task1, task2, task3);

此外,可以使用通用版本:

public async Task<T> FooMethod_1<T>(string sqlCode)
{
    using (SqlConnection connection = new SqlConnection(_connectionString)                  
        return await connection.ExecuteScalarAsync<T>(sqlCode);     
}

首先,对数据库并行执行许多命令不一定会更快,因为您给服务器增加了更多负载并使其尝试同时为所有内容提供服务,这只会导致每个查询阻塞另一个查询。

在单个连接上也很难做到这一点(除非你使用 MARS,它本身是不可取的),所以你需要多个连接。

无论如何,你根本不应该在这里进行多次查询。您可以通过一个查询一次性获取所有信息,性能与只执行其中一个相同。这是因为他们都是 select 来自同一个 table.

Note also that the catch block is redundant, and that you can use Dapper to retrieve multiple columns directly into your object.

public static async Task<ExampleData> GetExampleDataAsync()
{       
    using (SqlConnection connection = new SqlConnection(_connectionString))
    {
        return await connection.ExecuteScalarAsync<ExampleData>(@"
SELECT
    prop1 = COUNT(*),
    prop2 = COUNT(CASE WHEN row1 IS NULL THEN 1 END),
    prop3 = SUM(row2)
FROM dbo.ExampleTable;
        ");
    }
}