Search code examples
c#async-awaitdapper

Should I actually use async-await in inner methods?


Let's say I have 3 methods as follows:

public async Task<int> CalculateData(int index, int trustLevel, string type)
{
    var x = await CalculateBaseScore(index, type);
    return ComplexProcess(x, trustLevel);
}

public async Task<int> CalculateBaseScore(int index, string type)
{
    var conn_string = "...";
    var sql = "...";
    var param = new { Index = index, Type = type };
    using (var conn = new SqlConnection(conn_string))
    {
        var db_output = await conn.ExecuteScalarAsync<int>(sql, param: param);
        return db_output;
    }
}

public int CalculateBaseScore(int index, int trustLevel)
{
    //Omitted
}

As you can see, 2 of them use async-await.

In Stephen Cleary's introduction to async-await, it's said that

If you can write it without await, then you should write it without await, and remove the async keyword from the method. A non-async method returning Task.FromResult is more efficient than an async method returning a value.

In this case, will a Dapper-made database call qualify as a method I can write without async-await and simply return a Task<int> object? Will I get any benefits in performance or other aspects if I change it to this?

public async Task<int> CalculateData(int index, int trustLevel, string type)
{
    var x = await CalculateBaseScore(index, type);
    return ComplexProcess(x, trustLevel);
}

public Task<int> CalculateBaseScore(int index, string type)
{
    var conn_string = "...";
    var sql = "...";
    var param = new { Index = index, Type = type };
    using (var conn = new SqlConnection(conn_string))
    {
        var db_output_task = conn.ExecuteScalarAsync<int>(sql, param: param);
        return db_output_task;
    }
}

public int CalculateBaseScore(int index, int trustLevel)
{
    //Omitted
}

See tht in the second example, I omitted the async-await keywords on CalculateBaseScore and simply returned the Task object.


Solution

  • Regarding the second implementation without async-await:

    public Task<int> CalculateBaseScore(int index, string type)
    {
        var conn_string = "...";
        var sql = "...";
        var param = new { Index = index, Type = type };
        using (var conn = new SqlConnection(conn_string))
        {
            var db_output_task = conn.ExecuteScalarAsync<int>(sql, param: param);
            return db_output_task;
        }
    }
    

    ...will give you a performance boost of around 1 μsec per invocation. In other words it will save you a whole second of CPU time after invoking it 1,000,000 times. These are the good news.

    The bad news are that all these 1,000,000 invocations will result to an ObjectDisposedException, because the SqlConnection will be disposed prematurely, immediately after the creation of the Task.

    Does it worth it? I'd say no. Keeping it simple will save you more time in the long run.