I have the following piece of code whereby I first run a query and add the raw results ViewModel.RawContracts.AddRange(contractResults);
I then want to enrich this set of data with multiple different data sources e.g. _enrichmentHelper.ResolveSecurities(token, contractsToEnrich);
all of the enrichment within these methods are themselves done asynchronously.
Once all this enrichment has been completed I want to run a final piece of code to take the now enriched raw data to add it to my grid ViewModel.ContractRows.AddRange(ViewModel.RawContracts);
However my final Continuation queryAndEnrichmentTask
is executing once the query task completes rather than after each of the enrichment continuations complete.
What am I doing wrong here?
Task.Factory.StartNew
(
() =>
{
Log.Debug("Starting getting contracts");
Task queryTask = _serviceModel.GetContractsByCriteriaAsync(token, ViewModel.QueryRequest)
.LogExceptions()
.ContinueWith
(
prevTask =>
{
if (!token.IsCancellationRequested)
{
IQueryResponse response = null;
Log.Debug("Received contract response.");
if (prevTask.IsFaulted || (response = prevTask.Result) == null)
{
ViewModel.ErrorMessage = "Failed to load contracts. Please check the log file for details.";
}
else
{
if (response.Contracts != null)
{
Log.Debug($"Start loading {response.Contracts.Count()} contract positions...");
bool successful = true;
if (successful && prevTask.IsCompleted && prevTask.Result != null)
{
contractResults = prevTask.Result.Contracts.ToList();
ViewModel.RawContracts.Clear();
ViewModel.RawContracts.AddRange(contractResults);
}
Log.Debug("Finished loading contracts");
}
}
}
Log.Debug("Finished loading contracts");
}, token, TaskContinuationOptions.AttachedToParent | TaskContinuationOptions.NotOnCanceled, TaskScheduler.Default
); // End add raw results task
IList<IContract> contractsToEnrich = ViewModel.RawContracts;
queryTask.ContinueWith(prevTask =>
{
_enrichmentHelper.ResolveSecurities(token, contractsToEnrich);
}, token, TaskContinuationOptions.AttachedToParent | TaskContinuationOptions.OnlyOnRanToCompletion, TaskScheduler.Default);
queryTask.ContinueWith(prevTask =>
{
_enrichmentHelper.ResolveBooks(token, contractsToEnrich);
}, token, TaskContinuationOptions.AttachedToParent | TaskContinuationOptions.OnlyOnRanToCompletion, TaskScheduler.Default);
queryTask.ContinueWith(prevTask =>
{
_enrichmentHelper.ResolveCounterparties(token, contractsToEnrich);
}, token, TaskContinuationOptions.AttachedToParent | TaskContinuationOptions.OnlyOnRanToCompletion, TaskScheduler.Default);
queryTask.ContinueWith(prevTask =>
{
_enrichmentHelper.ResolveLegalEntities(token, contractsToEnrich);
}, token, TaskContinuationOptions.AttachedToParent | TaskContinuationOptions.OnlyOnRanToCompletion, TaskScheduler.Default);
}, token
)
.LogExceptions()
.ContinueWith
(
queryAndEnrichmentTask =>
{
Log.Debug("Post search task started");
if (queryAndEnrichmentTask.IsFaulted)
{
if (!ViewModel.HasErrorMessage)
ViewModel.ErrorMessage = "Error occured when loading data. Please refer to log file for details";
}
else
{
ViewModel.ContractRows.AddRange(ViewModel.RawContracts);
}
Log.Debug("Post search task completed");
}, token, TaskContinuationOptions.NotOnCanceled, TaskScheduler.Default)
.LogExceptions();
ContinueWith
. Useawait
to add continuations to tasks. It's far easier to write correctly, and makes it much less likely that you'll have hard to diagnose bugs in your code. There are very rarely situations whereContinueWith
is either needed or preferable. – ServyContinueWith
. Use the convention defined by your team, the library being worked with, and/or your own experience. – sean