A handful of out-of-the-box activities are vital to modeling the state-machine workflow type. With the exception of the EventDriven activity, all activities discussed in this section can be used only in a state machine.
As with the sequential workflow type, the state-machine workflow is represented as an activity. The StateMachineWorkflowActivity class inherits directly from CompositeActivity and implements the behavior that defines the concept of a state machine.
In contrast to the sequential workflow type, which can contain just about any activity as a direct child, the state-machine workflow can contain only State and EventDriven activities as direct children. These activity types and how they interact with the state machine are discussed in detail in the following sections.
The activity representing state machines has two properties that you use to configure vital behavior: InitialStateName and CompletedStateName. Both properties point to activities that are current children of the workflow. The InitialStateName property should point to the State activity, which is the first state that the workflow can be in (something like the Waiting For Order state in the previous example). Because a workflow logically has to have a first state, this property is required; otherwise, the workflow is in an invalid state.
The CompletedStateName property is equally useful and as important as the InitialStateName property. However, this property is not required, because it is conceivable to have a workflow that does not end. However, if you choose to have a completed state, the workflow knows it is finished whenever the referenced state is transitioned to. Most of the time, you specify a State activity in the CompletedStateName property, but there may be instances where it makes sense to have a never-ending workflow. For example, certain business processes might need to remain open for a long time, even if all work appears to have been completed. Keep in mind that just because the workflow has not completed, it does not need to be kept in memory. It can remain in a persisted state until more work needs to be performed.
The State activity is crucial to modeling a state machine because it represents the individual, discrete steps in a workflow. The State activity doesn’t do anything per se - it is a container for other activities that perform the work. The State activity is simply representative of a position or status in a workflow.
State activities have names such as WaitingForOrder, OnHold, and PendingApproval. In these examples, and most of the time, a State activity represents one status in which a workflow can be. However, a State activity can also contain other State activities. In these special cases, the parent activity can act as an event handler for all child activities. This is called recursive state composition and is discussed a little later in this chapter.
State activities can contain only four activity types. You already know that a State activity can contain other State activities, but the EventDriven, StateInitialization, and StateFinalization activities can also be children of the State activity. These activities are discussed next.
You use the EventDriven activity to define the events that can occur in the workflow states or in the state-machine workflow itself. Like the State activity, the EventDriven activity doesn’t really do anything by itself. Rather, because it inherits from CompositeActivity, it acts as a container for other activities that execute when the configured event is raised.
The EventDriven activity itself does not contain the facilities to point to a specific event to watch for. Events are listened for by using activities that implement the IEventActivity interface, such as the HandleExternalEvent and Delay activities. Therefore, one of these activities must be the first child of an EventDriven activity. Using an IEventActivity activity as the first child essentially blocks the execution of subsequent activities, which can be of any type, until the configured event fires - or, in the case of the Delay activity, when the timeout occurs. Furthermore, the EventDriven activity can contain only one activity that implements IEventActivity.
Each EventDriven activity can dictate which state the workflow transitions to upon the firing of the event and the subsequent execution of child activities. However, events can cause transitions only to State activities that are not parents to other State activities. These nonparent states are called leaf states. The nonleaf parent states act as a context container that accepts events for all child activities.
The StateInitialization activity is an optional component of the State activity, and there can be only one of this type of activity, if any. This activity inherits from SequenceActivity, so it can have multiple child activities, which are executed in order. If this activity is present in a state, it is automatically executed when its parent state is entered.
Because the StateInitialization activity is unconditionally executed at the point a State activity is transitioned to, it cannot contain any IEventActivity activities.
The StateFinalization activity has many of the traits of the StateInitialization activity. The most notable difference between the two activities is that the StateFinalization activity executes its child activities when the parent State activity is leaving by transitioning to another state.
Like the StateInitialization activity, the StateFinalization activity is optional. It cannot contain IEventActivity activities, and it inherits from SequenceActivity.
The SetState activity enables you to unconditionally transition to another state in the workflow without relying upon the firing of a specific event. However, there are restrictions on where this activity can be used. The SetState activity can be located only in an EventDriven or StateInitialization activity. Furthermore, in most cases, the SetState activity has to be the last child in the parent, because any other activities placed after it would never get executed. The exception to this rule is when the last child is an IfElse activity. In this case, the SetState activity has to be the last child in one of the branches.
Just like the EventDriven activity, the SetState activity can transition only to leaf states. You configure this activity by setting the TargetStateName property. Because of the way the state-machine workflow is meant to function, you should probably use the SetState activity sparingly, and state transitions should be dictated by events that are fired. However, this activity is available in the situations that warrant manual state transitions.