Beispiel #1
0
        /// <summary>
        /// Executes child nodes of the current node.
        /// </summary>
        /// <param name="context">Current ExecutionContext.</param>
        /// <returns>NodeResultStatus representing the current node result.</returns>
        protected sealed override async Task <NodeResultStatus> PerformExecuteAsync(IExecutionContext <TSource> context)
        {
            if (ChildNode == null)
            {
                Logger.LogWarning("Child node of TransitionNode doesn't exist, node will be skipped.");
                return(NodeResultStatus.NotRun);
            }

            Logger.LogDebug("Creating the TransitionNode destination subject.");
            TDestination destSubject = await TransitionSourceAsync(context).ConfigureAwait(false);

            var destContext = new ExecutionContext <TDestination>(destSubject, context.GlobalOptions);

            Logger.LogDebug("Preparing to execute TransitionNode child.");
            NodeResult destResult = await ChildNode.ExecuteAsync(destContext).ConfigureAwait(false);

            var exceptions = destResult.GetFailExceptions().ToList();

            if (exceptions.Count > 0)
            {
                Logger.LogInformation("TransitionNode child returned {0} exceptions.", exceptions.Count);
                context.ParentResult.Exception = exceptions.Count == 1 ? exceptions[0] : new AggregateException(exceptions);
            }
            Logger.LogDebug("Creating the TransitionNode destination result.");
            var resultSubject = await TransitionResultAsync(context, destResult).ConfigureAwait(false);

            if (!context.Subject.Equals(resultSubject))
            {
                Logger.LogDebug("Source subject has changed, calling ChangeSubject.");
                context.ChangeSubject(resultSubject);
            }

            return(destResult.Status);
        }
Beispiel #2
0
        /// <summary>
        /// Used to kick off execution of a node with a default execution context for all subjects using Async WhenAll semantics internally .
        /// </summary>
        /// <param name="subjects">Subject to be moved through the node.</param>
        /// <param name="options">Execution options to apply to running this enumerable of subjects.</param>
        /// <returns>An aggregated NodeResult.</returns>
        public async Task <NodeResult> ExecuteManyAsync(IEnumerable <T> subjects, ExecutionOptions options = null)
        {
            Guard.AgainstNullArgument("subjects", subjects);

            var nodeTimer = new NodeTimer();

            try
            {
                nodeTimer.LogStart(LogWriter, this, "ExecuteManyAsync");
                _processManyMode = true;
                Result           = new NodeResult(default(T), Id, FlowId);

                var subjectList = subjects.ToList();

                if (subjectList.Count == 0)
                {
                    return(Result);
                }

                if (options == null)
                {
                    options = new ExecutionOptions();
                }

                LogWriter.Debug("Running all subjects asynchronously.");

                Task aggregateTask = null;
                try
                {
                    var resultsQueue = new ConcurrentQueue <NodeResult>();
                    aggregateTask = subjectList.ForEachAsync(options.DegreeOfParallelism,
                                                             async x => resultsQueue.Enqueue(await ExecuteAsync(new ExecutionContext <T>(x, options))));

                    await aggregateTask;

                    Result.AddChildResults(resultsQueue);
                }
                catch
                {
                    if (options.ThrowOnError)
                    {
                        if (aggregateTask != null && aggregateTask.Exception != null)
                        {
                            throw aggregateTask.Exception;
                        }

                        throw;
                    }
                }

                ProcessExecuteManyResults(options);
                return(Result);
            }
            finally
            {
                _processManyMode = false;
                nodeTimer.LogStop(LogWriter, this, "ExecuteAsync");
            }
        }
Beispiel #3
0
        /// <summary>
        /// Used to kick off execution of a node with a default execution context for all subjects in a serial manner.
        /// </summary>
        /// <param name="subjects">Subject to be moved through the node.</param>
        /// <param name="options">Execution options to apply to running this enumerable of subjects.</param>
        /// <returns>An aggregated NodeResult.</returns>
        public async Task <NodeResult> ExecuteManySeriallyAsync(IEnumerable <T> subjects, ExecutionOptions options = null)
        {
            Guard.AgainstNullArgument("subjects", subjects);

            var nodeTimer = new NodeTimer();

            try
            {
                nodeTimer.LogStart(LogWriter, this, "ExecuteManySeriallyAsync");

                _processManyMode = true;

                var subjectList = subjects.ToList();

                Result = new NodeResult(default(T), Id, FlowId);

                if (subjectList.Count == 0)
                {
                    return(Result);
                }

                if (options == null)
                {
                    options = new ExecutionOptions();
                }

                foreach (var subject in subjectList)
                {
                    try
                    {
                        LogWriter.Debug("Running all subjects asynchronously in a serial manner.");

                        NodeResult result = await ExecuteAsync(new ExecutionContext <T>(subject, options)).ConfigureAwait(false);

                        Result.AddChildResult(result);
                    }
                    catch (Exception)
                    {
                        if (options.ThrowOnError)
                        {
                            throw;
                        }
                        if (!options.ContinueOnFailure)
                        {
                            break;
                        }
                    }
                }

                ProcessExecuteManyResults(options);

                return(Result);
            }
            finally
            {
                _processManyMode = false;
                nodeTimer.LogStop(LogWriter, this, "ExecuteAsync");
            }
        }
Beispiel #4
0
 /// <summary>
 /// Creates a new execution context.
 /// </summary>
 /// <param name="subject">Subject of the current flow.</param>
 /// <param name="globalOptions">Global options of the current flow.</param>
 /// <param name="rootResult">Root result if one has already been established.</param>
 public ExecutionContext(T subject, ExecutionOptions globalOptions = null, NodeResult rootResult = null)
 {
     State         = new DynamicDictionary();
     Subject       = subject;
     GlobalOptions = globalOptions ?? new ExecutionOptions();
     if (rootResult != null)
     {
         ParentResult = rootResult;
     }
 }
Beispiel #5
0
        /// <summary>
        ///     Prepares the execution context before the current node is run.
        /// </summary>
        /// <param name="context">Source context for preparation.</param>
        /// <param name="result">The result reference to add to the current context.</param>
        /// <returns>The execution context to be used in node execution.</returns>
        protected sealed override IExecutionContext <T> PrepareExecutionContext(IExecutionContext <T> context,
                                                                                NodeResult result)
        {
            Logger.LogDebug("Preparing execution context.");
            var resultContext = new ExecutionContext <T>(context, result);

            context.AddResult(result);

            return(resultContext);
        }
Beispiel #6
0
 /// <summary>
 /// Adds a result to the execution context.
 /// </summary>
 /// <param name="result">The result to add</param>
 public void AddResult(NodeResult result)
 {
     if (ParentResult == null)
     {
         ParentResult = result;
     }
     else if (ParentResult != result)
     {
         ParentResult.AddChildResult(result);
     }
 }
Beispiel #7
0
        /// <summary>
        /// Creates a child context based on the parent context.  Used for nesting multi-nodes inside of other nodes.
        /// </summary>
        /// <param name="parentContext">Parent of this context.</param>
        /// <param name="parentResult">Parent result to set on the new context, if any.</param>
        internal ExecutionContext(IExecutionContext <T> parentContext, NodeResult parentResult = null)
        {
            Guard.AgainstNullArgument("parentContext", parentContext);
            Guard.AgainstNullArgumentProperty("parentContext", "Subject", parentContext.Subject);
            Guard.AgainstNullArgumentProperty("parentContext", "GlobalOptions", parentContext.GlobalOptions);

            Subject          = parentContext.Subject;
            State            = parentContext.State;
            GlobalOptions    = parentContext.GlobalOptions;
            ParentResult     = parentResult;
            CancelProcessing = parentContext.CancelProcessing;
        }
Beispiel #8
0
        /// <summary>
        /// Executes child nodes of the current node.
        /// </summary>
        /// <param name="context">Current ExecutionContext.</param>
        /// <returns>NodeResultStatus representing the current node result.</returns>
        protected override async Task <NodeResultStatus> ExecuteChildrenAsync(IExecutionContext <T> context)
        {
            NodeResult result = null;

            Logger.LogDebug("Running first matching child node.");
            foreach (var childNode in Children)
            {
                result = await childNode.ExecuteAsync(context).ConfigureAwait(false);

                if (result.Status != NodeResultStatus.NotRun || context.CancelProcessing)
                {
                    break;
                }
            }

            if (result != null)
            {
                return(result.Status);
            }

            return(NodeResultStatus.NotRun);
        }
Beispiel #9
0
 /// <summary>
 /// Adds a child result to the current result.
 /// </summary>
 /// <param name="result">Result to add to child results.</param>
 internal void AddChildResult(NodeResult result)
 {
     _childResults.Enqueue(result);
 }
Beispiel #10
0
 /// <summary>
 /// Transitions the source based on the child result to prepare for return to the source flow.
 /// </summary>
 /// <param name="sourceContext">Context including the source subject.</param>
 /// <param name="result">The result of the destination node.</param>
 /// <returns>The transitioned subject.</returns>
 protected async override sealed Task <TSource> TransitionResultAsync(IExecutionContext <TSource> sourceContext, NodeResult result)
 {
     if (TransitionResultFuncAsync != null)
     {
         LogWriter.Debug("TransitionResultFuncAsync exists, using this function.");
         return(await TransitionResultFuncAsync(sourceContext, result).ConfigureAwait(false));
     }
     LogWriter.Debug("TransitionResultFuncAsync doesn't exist, returning original subject.");
     return(sourceContext.Subject);
 }
Beispiel #11
0
 /// <summary>
 /// Transitions the source based on the child result to prepare for.
 /// </summary>
 /// <param name="sourceContext">Context including the source subject.</param>
 /// <param name="result">The result of the destination node.</param>
 /// <returns>The transitioned subject.</returns>
 protected virtual Task <TSource> TransitionResultAsync(IExecutionContext <TSource> sourceContext, NodeResult result)
 {
     return(Task.FromResult(sourceContext.Subject));
 }
Beispiel #12
0
        /// <summary>
        /// Prepares the execution context before the current node is run.
        /// </summary>
        /// <param name="context">Source context for preparation.</param>
        /// <param name="result">The result reference to add to the current context.</param>
        /// <returns>The execution context to be used in node execution.</returns>
        protected virtual IExecutionContext <T> PrepareExecutionContext(IExecutionContext <T> context, NodeResult result)
        {
            LogWriter.Debug("Preparing the execution context for execution.");
            context.AddResult(result);

            return(context);
        }
Beispiel #13
0
        /// <summary>
        /// Used to kick off execution of a node with a specified execution context.
        /// </summary>
        /// <param name="sourceContext">ExecutionContext that includes a subject to be moved through the node.</param>
        /// <returns>A NodeResult</returns>
        public async Task <NodeResult> ExecuteAsync(IExecutionContext <T> sourceContext)
        {
            Guard.AgainstNullArgument("context", sourceContext);
            Guard.AgainstNullArgumentProperty("context", "Subject", sourceContext.Subject);

            var nodeTimer = new NodeTimer();

            try
            {
                nodeTimer.LogStart(LogWriter, this, "ExecuteAsync");

                if (Status != NodeRunStatus.NotRun)
                {
                    LogWriter.Debug("Status does not equal 'NotRun', resetting the node before execution");
                    Reset();
                }

                var subject = sourceContext.Subject;
                var result  = new NodeResult(subject, Id, FlowId);
                if (!_processManyMode)
                {
                    Result = result;
                }

                IExecutionContext <T> context = PrepareExecutionContext(sourceContext, result);

                OnBeforeExecute(context);

                if (!context.CancelProcessing)
                {
                    var effectiveOptions = GetEffectiveOptions(context.GlobalOptions);

                    if (!await ShouldExecuteInternalAsync(context).ConfigureAwait(false))
                    {
                        LogWriter.Info("ShouldExecute returned false, skipping execution");
                        return(result);
                    }

                    Status = NodeRunStatus.Running;
                    LogWriter.Debug("Executing the node");

                    try
                    {
                        result.Status = await PerformExecuteAsync(context).ConfigureAwait(false);

                        //Reset the subject in case it was changed.
                        result.Subject = context.Subject;
                        Status         = NodeRunStatus.Completed;
                        LogWriter.Info("Node completed execution, status is {0}", result.Status);
                    }
                    catch (Exception ex)
                    {
                        LogWriter.Error("Node erred during execution, status is Failed", ex);
                        Status           = NodeRunStatus.Faulted;
                        result.Subject   = context.Subject;
                        result.Status    = NodeResultStatus.Failed;
                        result.Exception = ex;

                        if (effectiveOptions.ThrowOnError)
                        {
                            throw;
                        }
                    }

                    OnAfterExecute(context);
                }

                sourceContext.CancelProcessing = context.CancelProcessing;

                return(result);
            }
            finally
            {
                nodeTimer.LogStop(LogWriter, this, "ExecuteAsync");
            }
        }