WF4 Advanced State Machine Stuff

So you want to know how the state machine really works?

Take a look at the WF4 State Machine Explorer.  This sample is designed to show you how StateMachine works with activities.

image

There are three transitions from State1.

  1. To State 2 which is triggered by a bookmark named “State2”
  2. To State 3 which is triggered by a bookmark named “State3”
  3. A Countdown transition which will count down from 10 seconds until it reaches zero at which point the condition is enabled and the workflow ends.

Now this can get a bit confusing but I think it is important to understand how the State Machine activity schedules child activities. 

Start to First Idle

  1. StateMachine will schedule the first state (State 1) – note you cannot have more than one first state
  2. State1 will execute and Tracking will receive a StateMachineStateRecord with the CurrentState set to State1
  3. State1 will schedule the Entry actions (if any)
  4. The Entry actions will execute
  5. When the Entry actions are complete, State1 will schedule the Trigger actions for each trigger in the order they are listed in the XAML (see <State.Transitions>)
  6. The trigger actions will execute in the order they were scheduled

Watch Out

In WF 3.5 the State Machine required that triggering activities implement a certain interface.  In WF4 there is no such requirement.  Any activity (or activities) can appear in a trigger.  This brings up some interesting possibilities.

Whenever there is more than one trigger, you have a race.  Each trigger is racing to complete and the first one which completes successfully and passes the condition wins.  The other triggers will be canceled when this happens.

What happens if a trigger has no activities?

This is known as a null trigger, trigger-less transition or unconditional transition.  It will immediately transition and cancel the others.  The State Machine designer will validate that your activity has only one of these.

What happens if a trigger has activities that don’ t cause the workflow to become idle?

The activities will run and then the condition will be evaluated.  If the condition fails, the activity will be scheduled to run again (and again) until the condition evaluates true or one of the other transitions completes and passes the condition test.  The transition that wins will cause the others to be canceled.

What if I want a transition that is conditional?

The WF4 State Machine design assume that the condition will be set by something that occurs in the trigger.  It will always run the activities in the trigger and then evaluate the condition.  If you don’t want to run the activities in the trigger you have to use control flow (like an If activity) to avoid this.

What happens if I resume a bookmark and one of the other triggers fires before the desired trigger can evaluate the condition?

If you create a trigger that is very busy (running several times a second) there is a good chance that none of your other triggers will ever get a chance to complete and cancel the busy trigger.

What happens if a transition does not pass the condition?

Transitions can have a condition associated with them.  If the Boolean expression evaluates to false, the trigger action will be scheduled again.  None of the other transitions will be affected.

How do you figure all this stuff out?

Tracking is your friend.  I’m in the process of moving my tracking extensions into Microsoft.Activities but right now they are in Microsoft.Activities.UnitTesting.  In the sample code you will notice that I have a simple class called Tracker

    1: internal class Tracker : TrackingParticipant
    2: {
    3:     protected override void Track(TrackingRecord record, TimeSpan timeout)
    4:     {
    5:         // Using extension method to get a human readable trace
    6:         record.Trace();
    7:     }
    8: }

Now all I need to do is to add an instance of this class to the Extensions collection

    1: var workflowDefinition = new AStateMachine() {EnableCountdown = EnableCountdown};
    2: var host = new WorkflowApplication(workflowDefinition) { Completed = (e) => completed = true };
    3:  
    4: // Setup the Tracking to output in the debug window
    5: host.Extensions.Add(new Tracker());

Now when I run my app I see tracking information in the VS Debug Output

 WorkflowInstance <AStateMachine> is <Started> at 05:06:21.1862
 Activity <null> is scheduled child activity <AStateMachine> at 05:06:21.1882
 Activity <AStateMachine> state is Executing at 05:06:21.1892
 {
     Arguments
         EnableCountdown: False
 }
 Activity <AStateMachine> is scheduled child activity <StateMachine> at 05:06:21.2052