I have a Quartz.NET recurring job based on cron expression in a .NET 8 Worker Service. The job is supposed to invoke 2 external APIs and then perform some processing on the API responses downstream.
To increase throughput, I have invoked the APIs using Task Parallel Library. So, I have to wait for both the tasks to finish before I can start processing. The implementation looks somewhat like this:
private void GetCarStatusAndPositionalInformation(
IJobExecutionContext context,
string terminalNumber)
{
CarInfo? carInfo = null; CarStatusInfo? carStatusInfo = null;
try
{
var getCarInformation = Task.Run(async () =>
{
var carInfoResponse = await _apiClient.QueryCarInformation(
terminalNumber, context.CancellationToken);
if (carInfoResponse?.CarInfo != null)
{
carInfo = carInfoResponse.CarInfo;
}
await Task.CompletedTask;
},
context.CancellationToken)
.ContinueWith(
continuationAction =>
{
_logger.LogWarning("Failed to get car info for terminal number: {terminalNumber}", terminalNumber);
},
context.CancellationToken,
TaskContinuationOptions.OnlyOnFaulted,
TaskScheduler.Current);
var getCarStatusInformation = Task.Run(async () =>
{
var carStatusInfoResponse = await _apiClient.GetCarStatusInfoAsync(
terminalNumber, context.CancellationToken);
if (carStatusInfoResponse?.CarStatusInfo != null)
{
carStatusInfo = carStatusInfoResponse.CarStatusInfo;
}
await Task.CompletedTask;
},
context.CancellationToken)
.ContinueWith(
continuationAction =>
{
_logger.LogWarning("Failed to get car info for terminal number: {terminalNumber}", terminalNumber);
},
context.CancellationToken,
TaskContinuationOptions.OnlyOnFaulted,
TaskScheduler.Current);
Task.WaitAll(
[getCarInformation, getCarStatusInformation], timeout: TimeSpan.FromSeconds(30));
if (carInfo != null) && carStatusInfo != null)
{
// Do some processing
}
}
catch (AggregateException aex)
{
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to obtain car status & positional information for: {terminalNumber}", terminalNumber);
}
}
Why am I always getting AggregateException with InnerException saying the tasks have been canceled?
The problem lies within the way you constructed the Tasks.
getCarInformationandgetCarStatusInformationactually reference the continuations, not the Tasks you were expecting.Now, the "fast and dirty" solution would be to take two steps:
But cleaner would be to not use
ContinueWithat all. Just move the failure-logging into the Task itself.Or just let the Exceptions bubble up. But be aware that this has some caveats. Especially if you need to handle both (if both fail). In that
WhenAll(and I am not sure aboutWaitAll) would then swallow exceptions.See: https://github.com/dotnet/core/issues/7011#issuecomment-988018459