/// <summary>
        /// Marks the <paramref name="job"/> to have the completed state of <paramref name="targetState"/>.
        /// </summary>
        /// <param name="job">The job to update.</param>
        /// <param name="targetState">The final completed state.</param>
        /// <param name="exception">The exception, if the state is failed.</param>
        protected void CompleteJob
        (
            AppTasks.ITaskProcessingJob <BackgroundOperationTaskDirective> job,
            MFTaskState targetState,
            Exception exception = null
        )
        {
            // Skip nulls.
            if (null == job)
            {
                return;
            }

            // Update the task.
            try
            {
                // Build up the task information with new data.
                var taskInformation = new TaskInformation(job.GetStatus()?.Data);
                taskInformation.CurrentTaskState = targetState;
                taskInformation.Completed        = DateTime.Now;
                taskInformation.LastActivity     = DateTime.Now;
                if (exception != null)
                {
                    taskInformation.StatusDetails = exception.Message;
                }

                // Update the task information.
                job.Update(taskInformation);
            }
            catch
            {
#if DEBUG
                throw;
#endif
            }
        }
        /// <summary>
        /// Handles when a job from a task requires processing.
        /// Note that all tasks/jobs in the queue use the same task type ID,
        /// so the directive <see cref="BackgroundOperationTaskDirective.BackgroundOperationName"/>
        /// is used to identify which tasks should be executed by
        /// which background operation.
        /// </summary>
        /// <param name="job">The job to process.</param>
        protected virtual void ProcessJobHandler(AppTasks.ITaskProcessingJob <BackgroundOperationTaskDirective> job)
        {
            // Sanity.
            if (null == job)
            {
                return;
            }

            // Ensure cancellation has not been requested.
            job.ThrowIfTaskCancellationRequested();

            // What is the current state?
            var state = job.GetStatus();

            // Sanity.
            if (null == job.Directive)
            {
                // This is an issue.  We have no way to decide what background operation should run it.  Die.
                SysUtils.ReportErrorToEventLog
                (
                    $"Job loaded with no directive (queue: {this.QueueId}, task type: {job.TaskInfo.TaskType}, task id: {job.TaskInfo.TaskID})."
                );
                return;
            }

            // Check that we know the job this was associated with.
            if (string.IsNullOrWhiteSpace(job.Directive.BackgroundOperationName))
            {
                // This is an issue.  We have no way to decide what background operation should run it.  Die.
                SysUtils.ReportErrorToEventLog
                (
                    $"Job loaded with no background operation name loaded (queue: {this.QueueId}, task type: {job.TaskInfo.TaskType}, task id: {job.TaskInfo.TaskID})."
                );
                return;
            }

            // If we have a directive then extract it.
            var dir = job.Directive.GetParsedInternalDirective();

            // Find the background operation to run.
            TaskQueueBackgroundOperation <TSecureConfiguration> bo = null;

            lock (_lock)
            {
                if (false == this.BackgroundOperations.TryGetValue(job.Directive.BackgroundOperationName, out bo))
                {
                    // We have no registered background operation to handle the callback.
                    SysUtils.ReportErrorToEventLog
                    (
                        $"No background operation found with name {job.Directive.BackgroundOperationName} (queue: {this.QueueId}, task type: {job.TaskInfo.TaskType}, task id: {job.TaskInfo.TaskID})."
                    );
                    return;
                }
            }

            // Should we repeat?
            DateTime?nextRun = null;

            switch (bo.RepeatType)
            {
            case TaskQueueBackgroundOperationRepeatType.Interval:

                // Add the interval to the current datetime.
                if (bo.Interval.HasValue)
                {
                    nextRun = DateTime.UtcNow.Add(bo.Interval.Value);
                }
                break;

            case TaskQueueBackgroundOperationRepeatType.Schedule:

                // Get the next execution time from the schedule.
                nextRun = bo.Schedule?.GetNextExecution(DateTime.Now);
                break;
            }

            // If we have a next run time then re-run.
            if (null != nextRun)
            {
                // Bind to the completed event ( called always ) of the job.
                // That way even if the job is canceled, fails, or finishes successfully
                // ...we always schedule the next run.
                job.Completed += (s, op)
                                 =>
                {
                    // Ensure that if two threads both run this at once we don't end up with a race condition.
                    lock (_lock)
                    {
                        // Cancel any future executions (we only want the single one created below).
                        this.CancelFutureExecutions(bo.Name);

                        // Now schedule it to run according to the interval.
                        this.RunOnce
                        (
                            bo.Name,
                            nextRun.Value.ToUniversalTime(),
                            dir
                        );
                    }
                };
            }

            // Bind to the life-cycle events.
            job.Succeeded += (sender, op) => CompleteJob(job, MFTaskState.MFTaskStateCompleted);
            job.Failed    += (sender, args) => CompleteJob(job, MFTaskState.MFTaskStateFailed, args.Exception);

            // Perform the action.
            // Mark it as started.
            job.Update
            (
                new TaskInformation()
            {
                Started          = DateTime.Now,
                LastActivity     = DateTime.Now,
                CurrentTaskState = MFTaskState.MFTaskStateInProgress
            }
            );

            // NOTE: this should not have any error handling around it here unless it re-throws the error
            // Catching the exception here can result in job.ProcessingFailed not being called
            // Delegate to the background operation.
            bo.RunJob
            (
                // The TaskProcessorJobEx class wraps the job and allows easy updates.
                new TaskProcessorJobEx <BackgroundOperationTaskDirective, TSecureConfiguration>()
            {
                Job = job,
                TaskQueueBackgroundOperationManager = this
            },
                dir
            );
        }