Beispiel #1
0
        /// <summary>
        /// Gets the current <see cref="WorkflowExecutionFrame"/> for the specified
        /// <see cref="IWorkflowSubject"/>.
        /// </summary>
        /// <param name="workflowSubject">
        /// Workflow subject to get execution frame for.
        /// </param>
        /// <param name="serviceProvider">
        /// Interface to service provider.
        /// </param>
        /// <returns>
        /// A <see cref="WorkflowExecutionFrame"/> object containing the current workflow execution
        /// state information for the specified <see cref="IWorkflowSubject"/>.
        /// objects.
        /// </returns>
        /// <exception cref="ArgumentNullException">
        /// Thrown when workflowSubject is null.
        /// </exception>
        /// <exception cref="StateNotFoundException">
        /// Thrown when the current state of the workflow subject cannot be
        /// found in the workflow.
        /// </exception>
        public WorkflowExecutionFrame GetExecutionFrame(IWorkflowSubject workflowSubject, IServiceProvider serviceProvider)
        {
            ///////////////////////////////////////////////////////////////////
            // Check arguments
            if (workflowSubject == null)
            {
                throw new ArgumentNullException("workflowSubject");
            }

            ///////////////////////////////////////////////////////////////////
            // Get the CURRENT state
            var currentStateName = workflowSubject.CurrentState;

            if (string.IsNullOrEmpty(currentStateName))
            {
                throw new StateNotFoundException(this, currentStateName);
            }

            var currentState = this.FindStateByName(currentStateName);

            if (currentState == null)
            {
                throw new StateNotFoundException(this, currentStateName);
            }

            var nextTransitions = currentState.Transitions.Select(t => new WorkflowTransitionDescriptor()
            {
                TransitionName = t.Name,
                IsAllowed      = t.IsAllowed(serviceProvider, workflowSubject.GetContextObject(serviceProvider))
            });

            return(WorkflowExecutionFrame.Create(workflowSubject, nextTransitions.ToList()));
        }
Beispiel #2
0
        /// <summary>
        /// Determines if the specified transition is allowed
        /// for the workflow subject.
        /// </summary>
        /// <param name="workflowSubject">
        /// Workflow subject to test
        /// </param>
        /// <param name="transitionName">
        /// Transition to test
        /// </param>
        /// <param name="serviceProvider">
        /// Service provider
        /// </param>
        /// <returns>
        /// Returns true if the transition is allowed, otherwise
        /// returns false
        /// </returns>
        /// <remarks>
        /// Checks condition associated with the transition against
        /// the context provided by the workflow subject.
        /// </remarks>
        public WorkflowExecutionResult IsTransitionToAllowed(IWorkflowSubject workflowSubject,
                                                             string transitionName,
                                                             IServiceProvider serviceProvider)
        {
            ///////////////////////////////////////////////////////////////////
            // Get the FROM state
            var currentStateName = workflowSubject.CurrentState;

            if (string.IsNullOrEmpty(currentStateName))
            {
                throw new StateNotFoundException(this, currentStateName);
            }

            var fromState = this.FindStateByName(currentStateName);

            if (fromState == null)
            {
                throw new StateNotFoundException(this, currentStateName);
            }

            var transition = fromState.GetTransition(transitionName);

            if (transition == null)
            {
                throw new TransitionNotFoundException(fromState, transitionName);
            }
            ///////////////////////////////////////////////////////////////////
            // Check to see if the transition is allowed
            if (!transition.IsAllowed(serviceProvider, workflowSubject.GetContextObject(serviceProvider)))
            {
                return(new WorkflowExecutionResult(WorkflowExecutionResultCode.NotAllowed, string.Format("Transition {0} not allowed {1}", transitionName, transition.ConditionErrorMessage)));
            }

            return(new WorkflowExecutionResult(WorkflowExecutionResultCode.Success));
        }
Beispiel #3
0
        /// <summary>
        /// Gets the collection of allowed transitions that are
        /// available for the given <see cref="IWorkflowSubject"/> from
        /// its <see cref="IWorkflowSubject.CurrentState"/>.
        /// </summary>
        /// <param name="workflowSubject">
        /// Workflow subject to get allowed transitions for.
        /// </param>
        /// <param name="serviceProvider">
        /// Interface to service provider.
        /// </param>
        /// <returns>
        /// A collection of <see cref="WorkflowTransition"/>
        /// objects.
        /// </returns>
        /// <exception cref="ArgumentNullException">
        /// Thrown when workflowSubject is null.
        /// </exception>
        /// <exception cref="StateNotFoundException">
        /// Thrown when the current state of the workflow subject cannot be
        /// found in the workflow.
        /// </exception>
        public IEnumerable <WorkflowTransition> GetAllowedTransitions(IWorkflowSubject workflowSubject, IServiceProvider serviceProvider)
        {
            ///////////////////////////////////////////////////////////////////
            // Check arguments
            if (workflowSubject == null)
            {
                throw new ArgumentNullException("workflowSubject");
            }

            ///////////////////////////////////////////////////////////////////
            // Get the CURRENT state
            var currentStateName = workflowSubject.CurrentState;

            if (string.IsNullOrEmpty(currentStateName))
            {
                throw new StateNotFoundException(this, currentStateName);
            }

            var currentState = this.FindStateByName(currentStateName);

            if (currentState == null)
            {
                throw new StateNotFoundException(this, currentStateName);
            }

            return(currentState.Transitions
                   .Where(t => t.IsAllowed(serviceProvider, workflowSubject.GetContextObject(serviceProvider))));
        }
Beispiel #4
0
        /// <summary>
        /// Starts execution of an <see cref="IWorkflowSubject"/> object
        /// in this workflow.
        /// </summary>
        /// <param name="workflowSubject">
        /// Workflow subject to start in this workflow.
        /// </param>
        /// <param name="serviceProvider">
        /// Interface to service provider.
        /// </param>
        /// <returns>
        /// Returns a <see cref="WorkflowExecutionResult"/> object
        /// that encapsulates the result of the operation.
        /// </returns>
        /// <remarks>
        /// This method associates the <see cref="IWorkflowSubject"/> with
        /// this workflow, sets the <see cref="IWorkflowSubject.CurrentState"/>
        /// to the <see cref="Workflow.InitialState"/> of this workflow, and
        /// executes the enter action of the initial state.
        /// </remarks>
        /// <exception cref="ArgumentNullException">
        /// Thrown when workflowSubject is null.
        /// </exception>
        /// <exception cref="InitialStateNotFoundException">
        /// Thrown when the workflow has no initial state defined.
        /// </exception>
        public WorkflowExecutionResult Start(IWorkflowSubject workflowSubject, IServiceProvider serviceProvider)
        {
            ///////////////////////////////////////////////////////////////////
            // Check arguments
            if (workflowSubject == null)
            {
                throw new ArgumentNullException("workflowSubject");
            }

            var initialState = this.InitialState;

            if (initialState == null)
            {
                throw new InitialStateNotFoundException();
            }

            ///////////////////////////////////////////////////////////////////
            // Execute EnterAction associated with the INITIAL state
            initialState.ExecuteEnterAction(serviceProvider, workflowSubject.GetContextObject(serviceProvider));

            ///////////////////////////////////////////////////////////////////
            // Assign the name of the workflow to the object
            workflowSubject.WorkflowName = this.FullName;

            ///////////////////////////////////////////////////////////////////
            // Set the current state of the workflow subject
            // to the INITIAL state
            workflowSubject.CurrentState = initialState.Name;

            ///////////////////////////////////////////////////////////////////
            // Invoke OnStarted callback
            workflowSubject.OnStarted(this);

            return(WorkflowExecutionResult.Success);
        }
Beispiel #5
0
        internal static WorkflowExecutionFrame Create(IWorkflowSubject subject,
                                                      IEnumerable <WorkflowTransitionDescriptor> nextTransitions)
        {
            var workflowExecutionFrame = new WorkflowExecutionFrame()
            {
                Subject         = subject,
                NextTransitions = nextTransitions
            };

            return(workflowExecutionFrame);
        }
        /// <summary>
        /// Gets the current <see cref="WorkflowExecutionFrame"/> for the specified
        /// <see cref="IWorkflowSubject"/>.
        /// </summary>
        /// <param name="workflowSubject">
        /// Workflow subject to get execution frame for
        /// </param>
        /// <returns>
        /// Returns a <see cref="WorkflowExecutionFrame"/> that describes the state of execution
        /// for the given <see cref="IWorkflowSubject"/>.
        /// </returns>
        public WorkflowExecutionFrame GetCurrentExecutionFrame(IWorkflowSubject workflowSubject)
        {
            if (workflowSubject == null)
            {
                throw new ArgumentNullException(nameof(workflowSubject));
            }

            var workflow = this.workflowByNameResolver.Resolve(workflowSubject.WorkflowName);

            if (workflow == null)
            {
                throw new WorkflowNotFoundException(workflowSubject.WorkflowName);
            }

            return(workflow.GetExecutionFrame(workflowSubject, this.serviceProvider));
        }
        /// <summary>
        /// Gets the collection of allowed transitions that are available
        /// for the given <see cref="IWorkflowSubject"/> from its
        /// <see cref="IWorkflowSubject.CurrentState"/>.
        /// </summary>
        /// <param name="workflowSubject">
        /// Workflow subject to get transitions for
        /// </param>
        /// <returns>
        /// Collection of <see cref="WorkflowTransition"/> objects
        /// that can be taken from the current state of the given
        /// <see cref="IWorkflowSubject"/> object.
        /// </returns>
        public IEnumerable <WorkflowTransition> GetAllowedTransitions(IWorkflowSubject workflowSubject)
        {
            if (workflowSubject == null)
            {
                throw new ArgumentNullException(nameof(workflowSubject));
            }

            var workflow = this.workflowByNameResolver.Resolve(workflowSubject.WorkflowName);

            if (workflow == null)
            {
                throw new WorkflowNotFoundException(workflowSubject.WorkflowName);
            }

            return(workflow.GetAllowedTransitions(workflowSubject, this.serviceProvider));
        }
        /// <summary>
        /// Transitions the specified workflow subject to a
        /// new state along a given transition.
        /// </summary>
        /// <param name="workflowSubject">
        /// <see cref="IWorkflowSubject"/> object to transition
        /// </param>
        /// <param name="transitionName">
        /// Name of transition to execute
        /// </param>
        /// <returns>
        /// Returns a <see cref="WorkflowExecutionResult"/> object
        /// that encapsulates the result of the operation.
        /// </returns>
        public WorkflowExecutionResult TransitionTo(IWorkflowSubject workflowSubject, string transitionName)
        {
            if (workflowSubject == null)
            {
                throw new ArgumentNullException(nameof(workflowSubject));
            }

            var workflow = this.workflowByNameResolver.Resolve(workflowSubject.WorkflowName);

            if (workflow == null)
            {
                throw new WorkflowNotFoundException(workflowSubject.WorkflowName);
            }

            return(workflow.TransitionTo(workflowSubject, transitionName, this.serviceProvider));
        }
Beispiel #9
0
        /// <summary>
        /// Starts execution of an <see cref="IWorkflowSubject"/> object
        /// in the workflow associated with the object.
        /// </summary>
        /// <param name="workflowExeService">
        /// Reference to the <see cref="IWorkflowExecutionService"/>
        /// to call
        /// </param>
        /// <param name="workflowSubject">
        /// <see cref="IWorkflowSubject"/> to start in the workflow
        /// </param>
        /// <returns>
        /// Returns a <see cref="WorkflowExecutionResult"/> object
        /// that encapsulates the result of the operation.
        /// </returns>
        /// <remarks>
        /// This is a short-cut method for <see cref="IWorkflowSubject"/>
        /// objects that already have a value assigned to
        /// <see cref="IWorkflowSubject.WorkflowName"/>.
        /// </remarks>
        public static WorkflowExecutionResult StartWorkflow(this IWorkflowExecutionService workflowExeService,
                                                            IWorkflowSubject workflowSubject)
        {
            if (workflowSubject == null)
            {
                throw new ArgumentNullException(nameof(workflowSubject));
            }

            var workflowName = workflowSubject.WorkflowName;

            if (string.IsNullOrEmpty(workflowName))
            {
                var msg = $"{workflowSubject} is not associated with a workflow";
                throw new InvalidOperationException(msg);
            }

            return(workflowExeService.StartWorkflow(workflowSubject, workflowName));
        }
Beispiel #10
0
        /// <summary>
        /// Gets the collection of allowed transition names that are available
        /// for the given <see cref="IWorkflowSubject"/> from its
        /// <see cref="IWorkflowSubject.CurrentState"/>.
        /// </summary>
        /// <param name="workflowExeService">
        /// Reference to the <see cref="IWorkflowExecutionService"/>
        /// to call
        /// </param>
        /// <param name="workflowSubject">
        /// Workflow subject to get transitions for
        /// </param>
        /// <returns>
        /// Returns a collection of transition names
        /// </returns>
        public static IEnumerable <string> GetAllowedTransitionNames(this IWorkflowExecutionService workflowExeService,
                                                                     IWorkflowSubject workflowSubject)
        {
            if (workflowSubject == null)
            {
                throw new ArgumentNullException(nameof(workflowSubject));
            }

            var workflowName = workflowSubject.WorkflowName;

            if (string.IsNullOrEmpty(workflowName))
            {
                var msg = $"{workflowSubject} is not associated with a workflow";
                throw new InvalidOperationException(msg);
            }

            return(workflowExeService.GetAllowedTransitions(workflowSubject)
                   .Select(t => t.Name));
        }
        /// <summary>
        /// Starts execution of an <see cref="IWorkflowSubject"/> object
        /// in the specified workflow.
        /// </summary>
        /// <param name="workflowSubject">
        /// <see cref="IWorkflowSubject"/> object to start in the
        /// workflow
        /// </param>
        /// <param name="workflowName">
        /// Fully-qualified name of the workflow to execute
        /// </param>
        /// <returns>
        /// Returns a <see cref="WorkflowExecutionResult"/> object
        /// that encapsulates the result of the operation.
        /// </returns>
        public WorkflowExecutionResult StartWorkflow(IWorkflowSubject workflowSubject, string workflowName)
        {
            if (workflowSubject == null)
            {
                throw new ArgumentNullException(nameof(workflowSubject));
            }

            if (string.IsNullOrEmpty(workflowName))
            {
                throw new ArgumentNullException(nameof(workflowName));
            }

            var workflow = this.workflowByNameResolver.Resolve(workflowName);

            if (workflow == null)
            {
                throw new WorkflowNotFoundException(workflowName);
            }

            return(workflow.Start(workflowSubject, this.serviceProvider));
        }
Beispiel #12
0
        /// <summary>
        /// Transitions the workflow subject to a new state along the
        /// specified transition.
        /// </summary>
        /// <param name="serviceProvider">
        /// Interface to service provider.
        /// </param>
        /// <param name="workflowSubject">
        /// Workflow subject to transition to a new state.
        /// </param>
        /// <param name="transitionName">
        /// Name of transition to follow.
        /// </param>
        /// <returns>
        /// Returns a <see cref="WorkflowExecutionResult"/> object
        /// that encapsulates the result of the operation.
        /// </returns>
        /// <exception cref="ArgumentNullException">
        /// Thrown when workflowSubject or transitionName is null.
        /// </exception>
        /// <exception cref="StateNotFoundException">
        /// Thrown when the current state of the workflow subject cannot be
        /// found in the workflow.
        /// </exception>
        /// <exception cref="TransitionNotFoundException">
        /// Thrown when the specified transitionName cannot be found
        /// in the FROM state.
        /// </exception>
        /// <exception cref="ActionFailedException">
        /// Thrown when an action fails exiting a state, transitioning,
        /// or entering a state.
        /// </exception>
        public static WorkflowExecutionResult TransitionTo(this IWorkflowSubject workflowSubject,
                                                           string transitionName,
                                                           IServiceProvider serviceProvider)
        {
            if (string.IsNullOrEmpty(transitionName))
            {
                throw new ArgumentNullException(nameof(transitionName));
            }

            if (serviceProvider == null)
            {
                throw new ArgumentNullException(nameof(serviceProvider));
            }

            var workflowName = workflowSubject.WorkflowName;

            if (string.IsNullOrEmpty(workflowName))
            {
                throw new InvalidOperationException("No workflow associated with this workflow subject");
            }

            var workflowResolver = serviceProvider.GetService(typeof(IWorkflowByNameResolver)) as IWorkflowByNameResolver;

            if (workflowResolver == null)
            {
                throw new ServiceNotFoundException(typeof(IWorkflowByNameResolver));
            }

            var workflow = workflowResolver.Resolve(workflowName);

            if (workflow == null)
            {
                var msg = string.Format("Workflow {0} not found", workflowName);
                throw new InvalidOperationException(msg);
            }

            return(workflow.TransitionTo(workflowSubject, transitionName, serviceProvider));
        }
Beispiel #13
0
        /// <summary>
        /// Transitions the specified workflow subject to a
        /// new state along a given <see cref="WorkflowTransition"/>.
        /// </summary>
        /// <param name="workflowSubject">
        /// Workflow subject to transition to a new state.
        /// </param>
        /// <param name="transitionName">
        /// Name of the <see cref="WorkflowTransition"/> to follow to
        /// the new state.
        /// </param>
        /// <param name="serviceProvider">
        /// Interface to service provider.
        /// </param>
        /// <returns>
        /// Returns a <see cref="WorkflowExecutionResult"/> object
        /// that encapsulates the result of the operation.
        /// </returns>
        /// <remarks>
        /// The transition is only allowed if the
        /// <see cref="WorkflowTransition.Condition"/> evaluates to true.
        /// The <see cref="WorkflowState.ExitAction"/>,
        /// <see cref="WorkflowTransition.Action"/>, and
        /// <see cref="WorkflowState.EnterAction"/> are all fired
        /// along the way.
        /// </remarks>
        /// <exception cref="ArgumentNullException">
        /// Thrown when workflowSubject or transitionName is null.
        /// </exception>
        /// <exception cref="StateNotFoundException">
        /// Thrown when the current state of the workflow subject cannot be
        /// found in the workflow.
        /// </exception>
        /// <exception cref="TransitionNotFoundException">
        /// Thrown when the specified transitionName cannot be found
        /// in the FROM state.
        /// </exception>
        /// <exception cref="ActionFailedException">
        /// Thrown when an action fails exiting a state, transitioning,
        /// or entering a state.
        /// </exception>
        public WorkflowExecutionResult TransitionTo(IWorkflowSubject workflowSubject, string transitionName, IServiceProvider serviceProvider)
        {
            WorkflowExecutionResult res = WorkflowExecutionResult.Success;

            ///////////////////////////////////////////////////////////////////
            // Check arguments
            if (workflowSubject == null)
            {
                throw new ArgumentNullException("workflowSubject");
            }

            if (string.IsNullOrEmpty(transitionName))
            {
                throw new ArgumentNullException("transitionName");
            }

            ///////////////////////////////////////////////////////////////////
            // Get the FROM state
            var currentStateName = workflowSubject.CurrentState;

            if (string.IsNullOrEmpty(currentStateName))
            {
                throw new StateNotFoundException(this, currentStateName);
            }

            var fromState = this.FindStateByName(currentStateName);

            if (fromState == null)
            {
                throw new StateNotFoundException(this, currentStateName);
            }

            ///////////////////////////////////////////////////////////////////
            // Get the transition
            var transition = fromState.GetTransition(transitionName);

            if (transition == null)
            {
                throw new TransitionNotFoundException(fromState, transitionName);
            }

            ///////////////////////////////////////////////////////////////////
            // Check to see if the transition is allowed
            if (!transition.IsAllowed(serviceProvider, workflowSubject.GetContextObject(serviceProvider)))
            {
                return(new WorkflowExecutionResult(
                           WorkflowExecutionResultCode.NotAllowed,
                           string.Format("Transition {0} not allowed {1}", transitionName, transition.ConditionErrorMessage)));
            }

            ///////////////////////////////////////////////////////////////////
            // Get the TO state
            var toState = (from s in this.States
                           where s.Name == transition.ToStateName
                           select s).FirstOrDefault();

            if (toState == null)
            {
                throw new StateNotFoundException(this, transition.ToStateName);
            }

            try
            {
                ///////////////////////////////////////////////////////////////////
                // Fire the pre-transition notification on the workflow subject
                workflowSubject.OnTransitioningTo(this, transition);

                CommandResult actionRes;

                ///////////////////////////////////////////////////////////////////
                // Execute ExitAction associated with the FROM state
                var exitActionTask = fromState.ExecuteExitAction(serviceProvider, workflowSubject.GetContextObject(serviceProvider));
                exitActionTask.RunSynchronously();
                actionRes = exitActionTask.Result;

                if (actionRes.IsSuccess)
                {
                    ///////////////////////////////////////////////////////////////////
                    // Execute Action associated with the transition
                    var transitionActionTask = transition.ExecuteAction(serviceProvider, workflowSubject.GetContextObject(serviceProvider));
                    transitionActionTask.RunSynchronously();
                    actionRes = transitionActionTask.Result;
                }

                if (actionRes.IsSuccess)
                {
                    ///////////////////////////////////////////////////////////////////
                    // Fire the post-transition notification on the workflow subject
                    workflowSubject.OnTransitionedTo(this, transition);
                }

                if (actionRes.IsSuccess)
                {
                    ///////////////////////////////////////////////////////////////////
                    // Set the current state of the workflow subject to the new state
                    workflowSubject.CurrentState = toState.Name;
                }

                if (actionRes.IsSuccess)
                {
                    ///////////////////////////////////////////////////////////////////
                    // Execute EnterAction associated with the TO state
                    var enterActionTask = toState.ExecuteEnterAction(serviceProvider, workflowSubject.GetContextObject(serviceProvider));
                    enterActionTask.RunSynchronously();
                    actionRes = enterActionTask.Result;
                }

                if (!actionRes.IsSuccess)
                {
                    res = new WorkflowExecutionResult(actionRes);
                }
            }
            catch (Exception ex)
            {
                res = new WorkflowExecutionResult(ex);
            }

            return(res);
        }