0

So I have a continuation defined:

var task = _httpClient.SendAsync(request, cts.Token);
task.ContinueWith(i => { /* TODO: log */ },
    TaskContinuationOptions.OnlyOnCanceled);
var response = await task.ConfigureAwait(false);

I get a compiler warning on the ContinueWith line:

Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call.

However, as you can see, I apply the await to the response.

When the method that contains this code exits, I get a NullReferenceException:

at System.Web.ThreadContext.AssociateWithCurrentThread(Boolean setImpersonationContext)
at System.Web.HttpApplication.OnThreadEnterPrivate(Boolean setImpersonationContext)
at System.Web.LegacyAspNetSynchronizationContext.CallCallbackPossiblyUnderLock(SendOrPostCallback callback, Object state)
at System.Web.LegacyAspNetSynchronizationContext.CallCallback(SendOrPostCallback callback, Object state)
at System.Threading.Tasks.AwaitTaskContinuation.RunCallback(ContextCallback callback, Object state, Task& currentTask)

My question is: how do I properly use task continuations and ConfigureAwait(false) at the same time?

Haney
  • 32,775
  • 8
  • 59
  • 68
  • [`ContinueWith`](https://msdn.microsoft.com/en-us/library/dd270696(v=vs.110).aspx) returns new task so there is nothing surprising about the warning... – Alexei Levenkov Feb 10 '15 at 03:54
  • @AlexeiLevenkov it's possible that I'm just missing some important knowledge here, as I've never really tried to use `ContinueWith` before. Any idea of how to properly use it with async/await? – Haney Feb 10 '15 at 04:04

2 Answers2

1

ContinueWith is what you used prior to async-await. Await automatically registers the rest of the method as a continueation so you don't need to do that. To achieve what you want with await, you can register a callback with the CancellationToken to log when it is cancelled.

CancellationTokenSource cts = new CancellationTokenSource();
cts.Token.Register(() => /* TODO: log */);  
var task =  _httpClient.SendAsync(request, cts.Token).ConfigureAwait(false);

ConfigureAwait(false) simply tells the compiler not to switch back to the captured synchronization context. If you used ContinueWith, then you can supply a TaskContinutationOptions.ExecuteSynchronously.

var task = _httpClient.SendAsync(request, cts.Token);
return task.ContinueWith(i => { /* TODO: log */ },
TaskContinuationOptions.OnlyOnCanceled | TaskContinutationOptions.ExecuteSynchronously);
NeddySpaghetti
  • 13,187
  • 5
  • 32
  • 61
0

It turns out that I was missing some vital information: exceptions will bubble up to your code per normal if you await the method causing the exception. So, all I have to do is this:

try
{
    await queue.AddMessageAsync(message).ConfigureAwait(false);
}
catch (OperationCanceledException)
{
    throw new Exception(string.Format("Unable to push message to queue {0}", queueName));
}

Since await is syntactic sugar for a continuation, the exception will be continued to it.

Haney
  • 32,775
  • 8
  • 59
  • 68
  • 1
    Right. The correct answer to `Any idea of how to properly use ContinueWith with async/await?` is to *replace* `ContinueWith` with `await`. – Stephen Cleary Feb 10 '15 at 14:00