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. Useawaitto 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 whereContinueWithis either needed or preferable.ContinueWith. Use the convention defined by your team, the library being worked with, and/or your own experience.