0
votes

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();
1
You should rarely, if ever, use ContinueWith. Use await 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 where ContinueWith is either needed or preferable.Servy
Disagree with never using ContinueWith. Use the convention defined by your team, the library being worked with, and/or your own experience.sean

1 Answers

1
votes

You create queryTask inside an outer task, but never await it. The outer task will return before queryTask completes. The ContinueWith() on the outer task will run when the outer task returns, and won't wait for queryTask to complete.

The smallest fix is to await queryTask before ending the task block that contains it. The outer task will still return but its Task will be set to an incomplete state, allowing ContinueWith() to do its job by awaiting it and then proceeding.

    await queryTask;  //<-- add this
)
.LogExceptions()
.ContinueWith
(
    queryAndEnrichmentTask =>