I'm building a .NET 4.0 application that uses ADO.NET, so I cannot use async/await. I don't want a solution for that, but I do want to know what of the following implementations is best and why. My unit tests pass for all three implementations, but I want to know the difference between these three.
#1 Nesting tasks
In my first implementation I wrap a task in another task. I think spinning up two tasks is bad for performance, but I'm not sure.
public virtual Task<IDataReader> ExecuteReaderAsync(IDbCommand dbCommand, CancellationToken cancellationToken)
{
return Task.Factory.StartNew(() =>
{
var sqlCommand = CheckIfSqlCommand(dbCommand);
PrepareExecuteReader(dbCommand);
return Task<IDataReader>
.Factory
.FromAsync(sqlCommand.BeginExecuteReader, sqlCommand.EndExecuteReader, null)
.Result;
}, cancellationToken);
}
#2 Using TaskCompletionSource
Then I tried wrapping the result in a TaskCompletionSource
so I just have one task.
public virtual Task<IDataReader> ExecuteReaderAsync(IDbCommand dbCommand, CancellationToken cancellationToken)
{
var taskCompletionSource = new TaskCompletionSource<IDataReader>();
var sqlCommand = CheckIfSqlCommand(dbCommand);
PrepareExecuteReader(dbCommand);
var reader = Task<IDataReader>
.Factory
.FromAsync(sqlCommand.BeginExecuteReader, sqlCommand.EndExecuteReader, null)
.Result;
taskCompletionSource.SetResult(reader);
return taskCompletionSource.Task;
}
#3 returning Task directly
My final solution is to directly return the task I created instead of wrapping it.
public virtual Task<IDataReader> ExecuteReaderAsync(IDbCommand dbCommand, CancellationToken cancellationToken)
{
var sqlCommand = CheckIfSqlCommand(dbCommand);
PrepareExecuteReader(dbCommand);
return Task<IDataReader>
.Factory
.FromAsync(sqlCommand.BeginExecuteReader, sqlCommand.EndExecuteReader, null);
}
So basically my question is:
What option should I use or is there a better way to do this?
Your #3 is the best. The first two introduce complication for no reason.
1 potentially adds another thread purely to run
CheckIfSqlCommand()
andPrepareExecuteReader()
asynchronously. This may be what you wanted, but they don't sound like commands that are going to take a long time.2 references
.Result
of the task, which will block until the task is complete, so defeats the whole purpose of using tasks.