HiveBrain v1.2.0
Get Started
← Back to all entries
patterncsharpMinor

Writing highly asynchronous code

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
asynchronouscodehighlywriting

Problem

I am writing a new web service that will be performing a lot of large data load operations. To do so I am moving the data to a temporary table then merging the data in to the live data via a stored procedure.

That part I have down and working and am very confident in. What I am doing now is trying to optimize the loading code to help take the load off of the IIS server that will be performing these operations.

Here is what I am currently doing synchronously and works fine.

public void BulkLoadData(IDataReaderWithMappings reader, 
                         string createTempTableStatement, 
                         string loadingStoredProcedureName)
{
    using (var connection = new SqlConnection(_connectionString))
    using (var bulkCopy = new SqlBulkCopy(connection))
    using (var command = new SqlCommand(createTempTableStatement, connection))
    {
        connection.Open();
        command.ExecuteNonQuery();
        bulkCopy.DestinationTableName = "#temp";
        var mappings = reader.GetMappings();
        foreach (var sqlBulkCopyColumnMapping in mappings)
        {
            bulkCopy.ColumnMappings.Add(sqlBulkCopyColumnMapping);
        }
        bulkCopy.WriteToServer(reader);
        command.CommandType = CommandType.StoredProcedure;
        command.CommandText = loadingStoredProcedureName;
        command.ExecuteNonQuery();
    }
}


And here is my attempt at translating that in to asynchronous code.

```
public async Task BulkLoadDataAsync(IDataReaderWithMappings reader,
string createTempTableStatement,
string loadingStoredProcedureName)
{
using (var connection = new SqlConnection(_connectionString))
using (var bulkCopy = new SqlBulkCopy(connection))
using (var command = new SqlCommand(createTempTableStatement))
{
var firstStage = connection.OpenAsync()
.ContinueWith(async delegate
{
await co

Solution

If you want to combine ContinueWith() and async this way, then you need to use Unwrap(). (And it is available, though my ReSharper doesn't seem to think so, maybe you use that too?) If you don't do that, you're not actually waiting for ExecuteNonQueryAsync() to complete.

But using ContinueWith() is usually not a good idea when you can use await. Instead, you can use a helper method:

private static async Task FirstStageAsync(SqlConnection connection, SqlCommand command)
{
    await connection.OpenAsync().ConfigureAwait(false);
    await command.ExecuteNonQueryAsync().ConfigureAwait(false);
}


And then in the main method:

var firstStage = FirstStageAsync(connection, command);

…

//Wait for the connection opening and the create table task to finish.
await firstStage.ConfigureAwait(false);


Another option would be use a lambda instead of the helper method:

Func firstStageFunc = async () =>
{
    await connection.OpenAsync().ConfigureAwait(false);
    await command.ExecuteNonQueryAsync().ConfigureAwait(false);
};

var firstStage = firstStageFunc();

Code Snippets

private static async Task FirstStageAsync(SqlConnection connection, SqlCommand command)
{
    await connection.OpenAsync().ConfigureAwait(false);
    await command.ExecuteNonQueryAsync().ConfigureAwait(false);
}
var firstStage = FirstStageAsync(connection, command);

…

//Wait for the connection opening and the create table task to finish.
await firstStage.ConfigureAwait(false);
Func<Task> firstStageFunc = async () =>
{
    await connection.OpenAsync().ConfigureAwait(false);
    await command.ExecuteNonQueryAsync().ConfigureAwait(false);
};

var firstStage = firstStageFunc();

Context

StackExchange Code Review Q#44547, answer score: 3

Revisions (0)

No revisions yet.