Beispiel #1
0
        /// <summary>
        /// Executes an <see cref="IJob"/>.
        /// </summary>
        /// <param name="token">The token to trigger cancellation.</param>
        /// <returns>A <see cref="Task"/> representing the asynchronous execution of this method.</returns>
        protected override async Task ExecuteInternalAsync(CancellationToken token)
        {
            Start(Job.Config.__filePath);

            var stopwatch = new Stopwatch();

            try
            {
                // ==========================
                // 1. Initialize
                // ==========================

                SetState(ExecutionState.Initializing);
                logger.LogDebug($"Calling {nameof(Job.InitializeAsync)}");
                stopwatch.Restart();
                Exception exception = null;

                try
                {
                    await Job.InitializeAsync();

                    logger.LogDebug($"{nameof(Job.InitializeAsync)} completed successfully");
                }
                catch (Exception ex) when(Job.Config.Execution.HandleExceptions)
                {
                    exception = ex;
                    logger.LogError(ex, $"{nameof(Job.InitializeAsync)} threw an excpetion: " + ex.ToString());
                }

                stopwatch.Stop();
                CompleteMethod(JobMethod.InitializeAsync, stopwatch.Elapsed, exception);

                if (exception != null)
                {
                    Fail(JobMethod.InitializeAsync, exception);
                    throw new ExecutionException(this.FailedIn.Value, exception);
                }

                // ==========================
                // 2. Get Items
                // ==========================

                SetState(ExecutionState.GettingItemsToProcess);
                logger.LogDebug($"Calling {nameof(source.GetItemsAsync)}");
                stopwatch.Restart();
                IAsyncEnumerable <TItem> items = null;

                try
                {
                    items = source.GetItemsAsync();

                    logger.LogDebug($"{nameof(source.GetItemsAsync)} completed successfully");
                }
                catch (Exception ex) when(Job.Config.Execution.HandleExceptions)
                {
                    exception = ex;
                    logger.LogError(ex, $"Unhandled exception in {nameof(source.GetItemsAsync)}");
                }

                stopwatch.Stop();
                CompleteMethod(JobMethod.GetItemsAsync, stopwatch.Elapsed, exception);

                if (exception != null)
                {
                    Fail(JobMethod.GetItemsAsync, exception);
                    throw new ExecutionException(this.FailedIn.Value, exception);
                }

                // ==========================
                // 3. Get Enumerator
                // ==========================

                logger.LogDebug($"Calling {nameof(items.GetAsyncEnumerator)}");
                stopwatch.Restart();

                try
                {
                    enumerator = items.GetAsyncEnumerator(Job.CancellationToken);

                    // If we're working with a Task<IEnumerable> wrapped in an
                    // AsyncEnumerableWrapper, let's await the task now.
                    if (enumerator is AsyncEnumeratorWrapper <TItem> )
                    {
                        await((AsyncEnumeratorWrapper <TItem>)enumerator).GetInnerEnumerator();
                    }

                    logger.LogDebug($"{nameof(items.GetAsyncEnumerator)} completed successfully");
                }
                catch (Exception ex) when(Job.Config.Execution.HandleExceptions)
                {
                    exception = ex;
                    logger.LogError(ex, $"Unhandled exception in {nameof(items.GetAsyncEnumerator)}");
                }

                stopwatch.Stop();
                CompleteMethod(JobMethod.GetEnumerator, stopwatch.Elapsed, exception);

                if (exception != null)
                {
                    Fail(JobMethod.GetEnumerator, exception);
                    throw new ExecutionException(this.FailedIn.Value, exception);
                }

                // ==========================
                // 4. Count Items
                // ==========================

                logger.LogDebug($"Calling items.Count");
                stopwatch.Restart();

                if (items is AsyncEnumerableWrapper <TItem> wrapper && wrapper.CanBeCounted)
                {
                    try
                    {
                        // If we're working with a Task<IEnumerable> wrapped in an AsyncEnumerableWrapper,
                        // we can count items using the inner enumerable (intialized above).
                        TotalItemCount = (await wrapper.GetInnerEnumerable()).Count();
                    }
                    catch (Exception ex) when(Job.Config.Execution.HandleExceptions)
                    {
                        exception = ex;
                        logger.LogError(ex, $"Unhandled exception in items.Count");
                    }
                }

                stopwatch.Stop();
                CompleteMethod(JobMethod.Count, stopwatch.Elapsed, exception);

                if (exception != null)
                {
                    Fail(JobMethod.Count, exception);
                    throw new ExecutionException(this.FailedIn.Value, exception);
                }

                // ==========================
                // 5. Process
                // ==========================

                SetState(ExecutionState.Processing);

                await ExecuteParallelTasksAsync();

                if (IsFailed)
                {
                    throw new ExecutionException(FailedIn.Value, FailedBecauseOf);
                }
            }
            catch (ExecutionException) when(Job.Config.Execution.HandleExceptions)
            {
                // This catch block is left empty intentionally. Do not remove!
                // When HandleExceptions is true the inner try/catch blocks will handle all
                // exceptions thrown from a job and record them in the results. After
                // recording the results an ExecutionException is thrown to abort the remaining
                // work (except when items are retried or the job failure count has not
                // been exceeded). Since the exception has already been recorded there is nothing
                // do to here except prevent this exception from propagating to the caller.
                // Finalize is guaranteed to be called, so it alone is in the finally block.
            }
            finally
            {
                // ==========================
                // 6. Finalize
                // ==========================

                SetState(ExecutionState.Finalizing);
                logger.LogDebug($"Calling {nameof(Job.FinalizeAsync)}");
                stopwatch.Restart();
                Exception exception = null;
                object    output    = null;

                try
                {
                    output = await Job.FinalizeAsync(Disposition);

                    logger.LogDebug($"{nameof(Job.FinalizeAsync)} completed successfully");
                }
                catch (Exception ex) when(Job.Config.Execution.HandleExceptions)
                {
                    exception = ex;
                    logger.LogError(ex, $"Unhandled excpetion in {nameof(Job.FinalizeAsync)}");
                }

                stopwatch.Stop();
                CompleteMethod(JobMethod.FinalizeAsync, stopwatch.Elapsed, exception);

                if (exception != null)
                {
                    Fail(JobMethod.FinalizeAsync, exception);
                }

                Complete(output);

                if (exception != null && !Job.Config.Execution.HandleExceptions)
                {
                    throw new ExecutionException(this.FailedIn.Value, exception);
                }
            }
        }