The implementation of the ExecuteNonQueryAsync()
method in System.Data.SqlClient.SqlCommand
is as follows:
public override Task<int> ExecuteNonQueryAsync(CancellationToken cancellationToken) {
Bid.CorrelationTrace("<sc.SqlCommand.ExecuteNonQueryAsync|API|Correlation> ObjectID%d#, ActivityID %ls\n", ObjectID);
SqlConnection.ExecutePermission.Demand();
TaskCompletionSource<int> source = new TaskCompletionSource<int>();
CancellationTokenRegistration registration = new CancellationTokenRegistration();
if (cancellationToken.CanBeCanceled) {
if (cancellationToken.IsCancellationRequested) {
source.SetCanceled();
return source.Task;
}
registration = cancellationToken.Register(CancelIgnoreFailure);
}
Task<int> returnedTask = source.Task;
try {
RegisterForConnectionCloseNotification(ref returnedTask);
Task<int>.Factory.FromAsync(BeginExecuteNonQueryAsync, EndExecuteNonQueryAsync, null).ContinueWith((t) => {
registration.Dispose();
if (t.IsFaulted) {
Exception e = t.Exception.InnerException;
source.SetException(e);
}
else {
if (t.IsCanceled) {
source.SetCanceled();
}
else {
source.SetResult(t.Result);
}
}
}, TaskScheduler.Default);
}
catch (Exception e) {
source.SetException(e);
}
return returnedTask;
}
Which I would summarize as:
- Create
TaskCompletionSource<int> source = new TaskCompletionSource<int>();
- Create a new task using
Task<int>.Factory.FromAsync
, using the APM "Begin/End" API- Invoke
source.SetResult()
when the task finishes.- Return
source.Task
What is the point of using TaskCompletionSource
here and why not to return the task created by Task<int>.Factory.FromAsync()
directly? This task also has the result and exception (if any) wrapped.
In C# in a Nutshell book, in the Asynchronous Programming and Continuations section, it states:
In writing Delay, we used TaskCompletionSource, which is a standard way to implement “bottom-level” I/O-bound asynchronous methods.
For compute-bound methods, we use Task.Run to initiate thread-bound concurrency. Simply by returning the task to the caller, we create an asynchronous method.
Why is it that the compute-bound methods can be implemented using Task.Run()
, but not the I/O bound methods?