Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#

Jump Start State Machine Workflow

3.27/5 (23 votes)
5 Apr 20077 min read 1   1.4K  
This is second in series of articles on Windows Workflow Foundation

Preface

This is second in series of articles on Windows Workflow Foundation that will help every Joe to start from ABC, and as this article series progresses, make steady rise to steeple of expertise. The first article of this series "Jump Start Windows Workflow Foundation" published last week.

Introduction

This article will start from where last article ended, though we will run a quick overview for buddies who joined our party from this article. Windows Workflow Foundation (WF, to save typing efforts) is one of four super stars that hit the limelight with .NET 3.0. It has a programming model, activity library, services, runtime a tool, an engine, and a design that helps programmers from separating sequence and state machine based logic from core business functionality. Critics have speculated about WF as one of the major changes in middle-tier since the arrival of COM+ / DTC and since Microsoft has plans for supporting it for current (Windows XP, 2003) and future (Vista) Windows releases; use and popularity of WF is only going to increase.

Looking around the software arena makes us realize almost all software solutions have certain tasks (call them activities) to perform either sequentially or on the basis of some events (call them state-machine based). Thus in our lingo, we will re-phrase above sentence as: almost every application has 'workflow' in it with number of 'activities' in it, these workflows may execute in a 'sequential' manner, or may be 'state-machine' based.

Summing this up, here are the precise definitions of terms we lately encountered.

  • Workflow: Series of steps, decisions and rules needed to complete a specific task.
  • Activities: Tasks/steps that define the workflow, these are basic building blocks of workflow.

There can be two types of workflows:

  • Sequential: series of activities are called in order.
  • State machine: activities are called on the basis of 'state' of different parameters.

In the last article, we made a sequential workflow application. This time we are here to create a 'state machine'-based application.

What and Why - State Machine workflows

"State machine" lit a bulb in the mind of many of us computer graduates and take us to those nice memories of university/college days. Well don't feel too nostalgic, come back to present days - you are going to learn some very exciting things today, so feel good about it and enjoy!

State machines have been in who's who of computer science since long. Programmers use them to model states, events and transitions. There are three key concepts involved here:

  1. State: a situation
  2. Event: a stimulus
  3. Transition: transfer of control from one 'state' to another, in consequence of some 'event'

Screenshot - image001.gif

In above diagram, control moved from 'State A' to 'State B' on occurrence of 'Event 1' with transition shown using arrow. Some action takes place after event is fired and transition is made. WF helps in programming solutions that have state machines in it by providing easy-to-used designer interface, activities and programming model.

Our Sample Scenario

  • We will develop a simple 'Purchase Ordering' based application. Each PO can have any of three states;
  • Created
  • Authorized
  • Sent

This will be modeled using WF Designer and simple code will be added to show how the life of a programmer is made easy by WF while programming event-driven (state machine-based) programs.

Welcome to the Playing field, let's start the game

Open up Visual Studio 2005 IDE and click on 'New Project'. Select 'State Machine Workflow Console Application' as project type.

Screenshot - image002.jpg

Select an appropriate project name (mine is 'FirstWFStateMachine'). Check 'Location' of project on the disk drive and click 'OK'. 'Workflow Designer' will open up now. Workflow will have only one 'State Activity' in it currently. Change its name to 'POInitialState' in 'Properties' dialog. Add three more 'State Activities' by dropping and dragging from the toolbox and name them as 'POCreated, POAuthorised, POSent'. Right-click on 'POInitialState' and select 'Select as Initial State', right-click 'POSent' and select 'Set as Completed State'. Your screen should look like the following now.

Screenshot - image003.jpg

That's done; we have defined all four 'states' in our program. Next, we will define 'events' that will be processed in each state. Now we will play with some code in our program. First define an interface for all three states.

C#
[ExternalDataExchange]
public interface IPOService
{
    event EventHandler<POStateChangedEventArgs>
    POCreated;
    event EventHandler<POStateChangedEventArgs>
    POAuthorised;
    event EventHandler<POStateChangedEventArgs>
    POSent;  
}

Next we will define our class for Purchase Order. It will have two attributes only (to keep it simple) Item ID and Due Delivery Date. Thus, a purchase order will be generated for a product only.

C#
[Serializable]
public class PO
{
    public PO() {}
    public PO(string _ItemID, DateTime _DueDeliveryDate)

    {

        ItemId = _ItemID;

        DueDeliveryDate = _DueDeliveryDate;
    }
    private string ItemId;
    private DateTime DueDeliveryDate;

    public string ItemCode
    {
        get { return ItemId; }
        set { ItemId = value; }
    }

    public DateTime PODueDeliveryDate
    {
        get { return DueDeliveryDate; }
        set { DueDeliveryDate = value; }
    }
}

Hold on a second here and let's ponder over why did we add [Serializable] and [ExternalDataExchange] above. Since it may take hours if not days, or even months for some PO to get authorized and sent (a Manager will catch a flight for Hawaii before authorizing PO!), worflows gets persisted on database. To achieve this behavior, one of the prerequisites is that your classes must be "serializable", thus the addition of [Serializable] directive. [ExternalDataExchange] must be added to our interface to denote it's a 'message contract' between runtime and workflow.

Look at IPOService interface again. POStateChangedEventArgs expects to have information for workflow to continue. Thus the object of PO will be passed to it.

C#
[Serializable]

public class POStateChangedEventArgs : ExternalDataEventArgs
{
    public POStateChangedEventArgs(Guid instanceID, PO po)
        : base(instanceID)
    {
        _PO = po;
        WaitForIdle = true;
    }

    private PO _PO;

    public PO PO
    {
        get { return _PO; }
        set { _PO = value; }
    }
}

Instance
Id
is GUID for each workflow instance (which subsequently means for each purchase order) created by runtime.

The service that implements the IPOService interface will raise events when the status of a PO changes. Let's implement it now and provide simple methods that raise events.

C#
public class POService : IPOService
{
    public event EventHandler<POStateChangedEventArgs> POCreated;
    public event EventHandler<POStateChangedEventArgs> POAuthorised;
    public event EventHandler<POStateChangedEventArgs> POSent;

    public void CreatePO(Guid id, PO po)
    {
        if (POCreated != null)
        { 
            POCreated(null, new POStateChangedEventArgs(id, po));
        }
    }

    public void AuthorisePO(Guid id, PO po)
    {
        if (POAuthorised != null)
        {
            POAuthorised(null, new POStateChangedEventArgs(id, po));
        }
    }

    public void SendPO(Guid id, PO po)
    {
        if (POSent != null)
        {
            POSent(null, new POStateChangedEventArgs(id, po));
        }
    }
}

Let's jump back to Workflow Designer now. We created a simple purchase ordering class, contracted between workflow and runtime (IPOService) and events. Now lets select 'Event Driven' activity from toolbox and drop at 'POInitialState'. Name it as 'OnPOCreate'. It should look like:

Screenshot - image004.jpg

'OnPOCreate' should have some series of activities that would be fired when this event is fired. For that, double-click it, this will open up details of the activity.

<Screenshot - image005.jpg

It also shows breadcrumb navigation at top-let corner of screen. Here we can see 'Event Driven' is just like any other sequential workflow, it can hold number of child activities. However, there is one restriction, the first activity within Event Driven activity must be Delay, HandleExternalEvent or WebServiceInput activity. This is because first activity must have implemented IEventActivity interface, which is only true for above mentioned three activities. Since we need to respond to external event (PO got created!) we will drag-drop the 'HandleExternalEvent' activity. Change its name to 'handlePOCreatedEvent', click on ellipses to select Interface 'IPOService', select 'Eventype' from drop down.

Screenshot - image006.jpg

Screenshot - image007.jpg

Now, select 'Set State' activity and drag-drop this on designer window. In properties window, assign 'POCreatedState' as 'Target state'. This will create a link from 'POInitialState' to 'POCreatedState'.

Screenshot - image008.jpg

Following same procedure, we will model other state transitions in Workflow. In the end, it will look like following:

Screenshot - image009.jpg

Can you believe, we are almost done! Just add this simple code in Program.cs that will act as runtime host. We will add simple Console.ReadLine and Console.WriteLine statements to create console interactive environment, where user is prompted for item ID and due delivery date for creating a purchase order. Then user is prompted for PO authorization, and until she says yes to it, PO's state doesn't change. Similar is the case with PO sent. In the end, we added a simple method for printing current and possible future transitions at each state of Workflow. This is achieved using 'PossibleStateTransitions' collection in Workflow instance.

C#
private static void DumpStateMachine(WorkflowRuntime runtime, Guid instanceID)
{
    StateMachineWorkflowInstance instance =
        new StateMachineWorkflowInstance(runtime, instanceID);

    Console.WriteLine("Workflow ID: {0}", instanceID);
    Console.WriteLine("Current State: {0}",

        instance.CurrentStateName);

    Console.WriteLine("Possible Transitions: {0}",
        instance.PossibleStateTransitions.Count);
    foreach (string name in instance.PossibleStateTransitions)
    {
        Console.WriteLine("\t{0}", name);
    }
}

Following are screen shots of running application:

Screenshot - image010.jpg

Screenshot - image011.jpg

What We Learned

This article followed a jump start approach to explore 'State-Machine' based workflows. We picked a practical problem and tried resolving it using WF by defining states (using State activity), events (Event Driven activity and Handle External Event activity) and transitions (SetState activity). A simple console application hosted our workflow runtime to provide interactive feel to user.

Where Can I Find More?

Hopefully this is enough to ignite the technology passion in you, so here are more donuts for your taste buds:

  1. Programming Workflow Foundation – Practical WF Techniques and Examples using XAML and C# by K. Scott Allen
  2. K. Scott Allen's blog
  3. Essential Windows Workflow Framework by DharmaShukla, BobSchmidt.
  4. Official Home of Workflow Foundation in webspace
  5. Google! – Search and find more articles/blog posts appearing in the techie arena everyday.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here