/// <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())); }
/// <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)); }
/// <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)))); }
/// <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); }
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)); }
/// <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)); }
/// <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)); }
/// <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)); }
/// <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); }