Windows Workflow Foundation provides the basic infrastructure to monitor and manipulate state-machine workflows. Monitoring is useful when you’d like to see where a running workflow is in its execution lifecycle. Manipulation comes in handy when outside input dictates that a state machine should take an alternative path. The following sections cover these topics.
Because the state-machine workflow type is fundamentally unique, it has its own instance wrapper class that provides a wealth of data describing a running workflow instance as well as functionality to interact with the running workflow. This class is called StateMachineWorkflowInstance and can be found in the System.Workflow.Activities namespace.
To acquire an instance of this class for use in your workflow host, you need an instance of the WorkflowRuntime class and an InstanceId of the workflow instance of interest. The following code shows how to create an instance of StateMachineWorkflowInstance:
WorkflowRuntime runtime = new WorkflowRuntime(); WorkflowInstance instance = runtime.CreateWorkflow(typeof(MyWorkflow)); instance.Start(); stateMachineInstance = new StateMachineWorkflowInstance(runtime, instance.InstanceId);
Obviously, the workflow definition represented by the WorkflowInstance class needs to be a state-machine workflow; otherwise, the constructor throws an ArgumentException.
This class has properties that enable you to get information about the workflow instance and the workflow definition. The WorkflowInstance property returns an instance of the class of the same name. The StateMachineWorkflow property returns an instance of StateMachineWorkflowActivity, which is the definition of the workflow.
The StateMachineWorkflowInstance class also contains quite a few properties that enable you to query the workflow’s current status. These properties and their uses are discussed in the next section.
The StateMachineWorkflowInstance class gives you a great deal of information regarding the current state of a state-machine workflow instance. For example, you might have the following questions about the state machine:
What are the states that make up the workflow?
What state is the workflow currently in?
Given the current state, what states can be next?
What has the workflow done in the past?
To answer these questions, the StateMachineWorkflowInstance class provides you with several public properties. First, the States property returns a list of State activities through a ReadOnly Collection<StateActivity> instance, which represents all the states that make up the workflow.
To determine the state that the workflow is currently in, you can use the CurrentStateName or CurrentState property. The CurrentState property returns a string that represents the current state’s name. The CurrentState property returns the State activity instance that represents the current state.
The third question regarding the available state changes can be answered by the PossibleState Transitions property. This property is of type ReadOnlyCollection<string>, thereby providing the list of possible states in string form.
The ability to access all this information is very useful, especially when you want to create a UI to monitor and interact with state-machine workflows. Imagine an application that has certain user interface components available when the workflow is in a given list of states. If information about the current workflow state were not available, you would have to implement duplicate logic in the client application to know when certain UI components should be available or enabled. However, the ability to query enables you to keep all the process logic in the workflow, where it should be. All the client application has to do is ask the workflow what state it is in and what states can come next. Logic can then be implemented to make the appropriate UI components available to the user.
Another way in which the StateMachineWorkflowInstance class provides flexibility in managing workflows is by allowing the client to programmatically set the current state of a workflow instance. This functionally is achieved by calling the SetState method and passing either a string representation of a state or an instance of a StateActivity class.
You should employ great restraint when using this method, because it basically sets a workflow’s state using brute force. This means that no matter where the workflow is in its execution, and no matter what state the workflow is currently in, the SetState method obeys the command given by the code. It is important to let the workflow manage its own state; otherwise, the integrity of logic the workflow can degrade.
With that said, there are instances when using the SetState method is appropriate. Think of this method as an override option for certain steps in a workflow.
Imagine a workflow that manages certain processing states of data cleansing. Messages that come in to the workflow from outside sources might need to be processed into a common format. In this case, the workflow could have several states that represent different buckets of processing progress as well as exception states for when data cannot be easily transformed. For example, when an exception state is reached, the workflow application could notify the appropriate employee and ask him or her to manually fix the problem. However, there might be cases where the employee, who should be an expert in the data, decides that the message looks good enough to progress and decides to skip the manual step. In such a case, the SetState method provides a great alternative to modeling every manual override in the workflow itself. The client application used by the data analyst employees could have a button that enables them to override the manual intervention state, which then lets the data processing proceed as normal.
Although the SetState method can provide a great way to manage manual overrides and other workflow exceptions, it should be used only when necessary. As a rule of thumb, you should model override scenarios on the workflow itself rather than use this method. However, if accounting for the situations that might occur becomes daunting or starts to feel unnatural, perhaps the SetState method is a good candidate.