Reputation: 1249
My code is similar to this:
public async Process()
{
try
{
var records = await this.provider.GetRecordsAsync(); // this method can throws 'DataStoreException' only
foreach(var record in records)
{
try
{
this.Do(record); // here is some exception and code goes to catch
record.Status = Status.Success;
await this.provider.UpdateAsync(record).ContinueWith(
task =>
{
task.Exception?.Handle(e =>
{
this.logger.LogError(e);
return true;
});
}, TaskContinuationOptions.NotOnRanToCompletion);
}
catch(Exception e)
{
record.Status = Status.Error;
/*line 106:*/ await this.provider.UpdateAsync(record).ContinueWith(
task =>
{
task.Exception?.Handle(e =>
{
this.logger.LogError(e);
return true;
});
}, TaskContinuationOptions.NotOnRanToCompletion);
}
}
}
catch (DataStoreException e)
{
this.logger.LogError(e);
}
catch (Exception e)
{
// Got TaskCanceledException here
}
}
And at commented point I got TaskCanceledException
. I suppose the reason are mixed await
and ContinueWith
, but can some one explain, what happened here? UpdateAsync
can throw DataStoreException
only. So TaskCanceledException
thew by ContinueWith
, but there is no cancellation tokens, code do not even reach this point task.Exception?.Handle
Here is the CallStack:
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
at ProjectTest.MyClass.<Process>d__6.MoveNext() in C:\Projects\ProjectTest\MyClass.cs:line 106
If try/catch
surrounds this.provider.UpdateAsync(record)
instead of ContinueWith
everything is good and code works as expected.
Does it actually bad practice to mix await
and ContinueWith
and if so, why?
Upvotes: 1
Views: 519
Reputation: 156459
The behavior you're seeing can be reproduced much more simply:
await Task.CompletedTask.ContinueWith(task =>
{
}, TaskContinuationOptions.NotOnRanToCompletion);
TaskContinuationOptions.NotOnRanToCompletion
instructs the ContinueWith
method to not execute the callback if the task completes successfully, and instead return a cancelled task.
You would use that option if you have a continuation that you specifically expect to run when an error occurs. For example:
await Task.FromException(new Exception()).ContinueWith(task =>
{
Console.WriteLine("Task did not execute to completion");
}, TaskContinuationOptions.NotOnRanToCompletion);
Since it looks like you expect this.provider.UpdateAsync(record)
might succeed, that's probably not the option you want to use.
Upvotes: 1