private async Task ProcessLoadDutyPlanRequestAsync(ApiRequestContext <LoadDutyplanRequest> requestContext) { LoadDutyplanRequest request = requestContext.Request; // fetch and wait for employment data first. it is prerequisite for next parallelized steps ApiResponse <LoadDutyplanRequest, EmploymentShard[]> employmentShardsResponse = await this.employmentShardRef .Ask <ApiResponse <LoadDutyplanRequest, EmploymentShard[]> >(requestContext) .ConfigureAwait(continueOnCapturedContext: true); if (employmentShardsResponse.Error) { this.Log($"Cannot process employments for '{request}'. Skipping rest of pipeline"); return; } // wrapped original request with additional context data for actors to work properly ApiRequestContext <LoadDutyplanRequest, long[]> requestContextWithEmployments = requestContext.Change( employmentShardsResponse .Result .Select(x => x.Id) .ToArray()); // spin further actions to further actors as parallel. // they will respond when ready to. Task <ApiResponse>[] steps = new Task <ApiResponse>[] { // duty and deviation data this.dutiesDeviationsShardRef.Ask <ApiResponse>(requestContextWithEmployments), // calculations data this.normtimeShardRef.Ask <ApiResponse>(requestContextWithEmployments), // absence data this.absenceShardRef.Ask <ApiResponse>(requestContextWithEmployments), // validations data this.validationShardRef.Ask <ApiResponse>(requestContextWithEmployments), }; // await for all operations to finish until next message will be processed from queue await Task.WhenAll(steps.ToArray()); int stepsOk = steps.Count(x => x.IsCompletedSuccessfully && x.Result.Success); this.Log($"All parts of {nameof(LoadDutyplanRequest)}.ID = {request.Id} finished. {stepsOk} out of {steps.Length} succeeded"); }