Introduction

This article describes how to create and run a State Machine Workflow in Windows Workflow Foundation (formerly WWF, or just WF) which comes bundled with .NET Framework 3.0 and above.

Before we get started, if you are new to WF and .NET Framework 3.0, I think you'd better take a look at some articles about WF. Here are some useful links:

Background

In order to create WF applications, you need to install some required packages/tools.

These are:

  1. .NET Framework 3.0 Runtime
  2. Visual Studio 2005 Professional Edition
  3. Windows Workflow Foundation extensions for Visual Studio 2005
  4. Windows Communication Foundation (WCF,formerly Indigo) & Windows Presentation Foundation (WPF,formerly Avalon)

The fourth one is optional in fact. However, there may be some troubles and you may get some weird error if you don't have WCF and WPF installed on your development computer. So, I suggest you have WCF and WPF installed too.

Now, I want to demonstrate a daily life example. Assume that you want to lock something (A file, a safe, a door,...etc.). This locker has exactly 2 different states: LOCKED and UNLOCKED. And you change its state by opening and closing. In our application, we call some methods to change workflows state; from/to LOCKED to/from UNLOCKED.

Using the Code

Let's dig in the code.

First we open .NET Visual Studio 2005. In the Start Page window, click Project from Create row. In the New Project window, extend Visual C# node, then select Workflow. There are many applications but we will create a state machine application so we select State Machine Workflow Library and name it FirstStateMachineWorkflow. Then click OK.

After creating the application, your designer window should look like this:

NOTE: If you can't see Workflow1.cs[Design] or if there is something wrong with the steps above, go check the requirements. Probably you missed some of them or they are not installed properly.

Now, we need an interface to declare the events. Right click the project -> Add -> New Item -> Select Interface and name it ILocker.cs. Write the code below to the interface.

Collapse Copy Code

using System;

using System.Collections.Generic;

using System.Text;

using System.Workflow.Activities;

namespace FirstStateMachineWorkflow

{

[ExternalDataExchange]

publicinterface ILocker

{

event EventHandler<ExternalDataEventArgs> Lock;

event EventHandler<ExternalDataEventArgs> Unlock;

}

}

Lock and Unlock are our events which make the workflow change its state. Now we need a class implementing this interface and having the methods to fire the events. Right click the project -> Add -> New Item -> Select Class and name it Locker.cs. Write the code given below to the class:

Collapse Copy Code

using System;

using System.Collections.Generic;

using System.Text;

using System.Workflow.Activities;

namespace FirstStateMachineWorkflow

{

[Serializable]

publicclass Locker : ILocker

{

publicevent EventHandler<ExternalDataEventArgs> Lock;

publicevent EventHandler<ExternalDataEventArgs> Unlock;

publicbool lockerState = false;

publicbool LockIt(Guid InstanceID)

{

if (this.Lock != null)

{

this.Lock(this,

new System.Workflow.Activities.ExternalDataEventArgs(InstanceID));

returntrue;

}

else

{

returnfalse;

}

}

publicbool UnlockIt(Guid InstanceID)

{

if (this.Unlock != null)

{

this.Unlock(this,

new System.Workflow.Activities.ExternalDataEventArgs(InstanceID));

returnfalse;

}

else

{

returntrue;

}

}

}

}

As you see Locker class uses events to change the workflows state. These two methods (LockIt and UnlockIt) are going to be used by the host application.

Now, we go back to our workflow designer window. Open Workflow1.cs[Designer]. Click the state and in the properties window, change its name as Unlocked. On the right-hand side tool box, drag and drop EventDrivenActivity on the state. Change its name as LockEvent and double-click. This will open a new design window. On the toolbox, drag and drop HandleExternalEventActivity and change its name to HandleLockEvent. This will help us to handle the event fired by the host application. While HandleLockEvent is selected, in the properties window, click the InterfaceType property. A new window will open, there, select ILocker. This is needed to define which events are handled by which event handler. Now, above in the properties window, select Lock in the EventName combo-box. So far, we made the Lock event to be handled by this handler. But after handling, we want to write a message to the console stating which event is handled. To do this, drag and drop a CodeActivity from toolbox, name it LockedCode, double click it. You will go to Code View. There in the LockedCode_ExecuteCode method, write the code below:

Collapse Copy Code

privatevoid WriteState_ExecuteCode(object sender, EventArgs e)

{

Console.WriteLine("Locked!!!");

}

So, we handled the event, wrote the message, what next? Now we must do something to change the current state. From toolbox drag and drop a new StateActivity to Workflow1.cs[Design] and name it as Locked. Now in HandleLockedEvent view, drag and drop SetStateActivity under code activity. Name it as SetLocked and in the properties window, set TargetStateName as Locked.

Do the same steps for Locked state reversed and properly. Drag drop HandleUnlockEvent, add UnlockedCode and SetUnlocked to Unlocked.

After all these changes, your Workflow1.cs[Designer] should look like this :

Now it is time to add a host application to the solution.

NOTE : Remember, Windows Workflows (be it State Machine or Sequential Workflow) cannot run by themselves. They need an external host application. The application may be a Windows application, Console Application or Web Application (ASP.NET).

Right click solution -> Add -> New Project. In the New Project window, select Visual C# and select Console Application , name it Test.

After creating the console application, right click on the console application and select Set AsStartUp Project. We must add reference to use workflow methods. To do that, right click References -> Add Reference -> In .NET tab, select System.Workflow.Activities, System.Workflow.Runtime, System.Workflow.ComponentModel.

We need FirstStateMachineWorkflow reference also. References -> Add Reference -> In Projects tab, select FirstStateMachineWorkflow and click OK.

In Program.cs, write the code given below to run the workflow:

Collapse Copy Code

using System;

using System.Collections.Generic;

using System.Text;

using System.Workflow;

using System.Workflow.Activities;

using System.Workflow.Runtime;

using FirstStateMachineWorkflow;

namespace Test

{

class Program

{

staticvoid Main(string[] args)

{

WorkflowRuntime workflowruntime = new WorkflowRuntime();

Locker locker = new Locker();

Guid locker_ID = Guid.NewGuid();

Dictionary<string,object> Params = new Dictionary<string,object>();

ExternalDataExchangeService ExDateExchService =

new ExternalDataExchangeService();

workflowruntime.AddService(ExDateExchService);

ExDateExchService.AddService(locker);

WorkflowInstance wfinstance = workflowruntime.CreateWorkflow(

typeof(Workflow1) , Params , locker_ID );

wfinstance.Start();

locker.LockIt(locker_ID);

Console.ReadLine();

locker.UnlockIt(locker_ID);

Console.ReadLine();

}

}

}

Here, WorkflowRuntime object is used to configure workflow properties, Locker is our class implementing interface. Dictionary is to pass parameters to workflow. However, we don't have any parameters in our example.

Collapse Copy Code

WorkflowInstance wfinstance = workflowruntime.CreateWorkflow(

typeof(Workflow1) , Params , locker_ID );

wfinstance.Start();

WorkflowInstance is used to create an instance of our Workflow1 workflow and then it is started with the wfinstance.Start method.

After running this application, you should get this console window:

Points of Interest

As I mentioned before, WF itself is not enough to create WF applications. You may have WCF and WPF installed as well as WF.

And to summarize the steps again:

  1. Create a workflow statemachine application.
  2. Write an Interface for transitions between states (Write events).
  3. Write the [Serializable] class implementing the interface events and calling them
    in functions.
  4. Create a host application which uses the class and starts workflow.

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'

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.

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.

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.

Collapse Copy Code

[ExternalDataExchange]

publicinterface 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.

Collapse Copy Code

[Serializable]

publicclass PO

{

public PO() {}

public PO(string _ItemID, DateTime _DueDeliveryDate)

{

ItemId = _ItemID;

DueDeliveryDate = _DueDeliveryDate;

}

privatestring ItemId;

private DateTime DueDeliveryDate;

publicstring 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.

Collapse Copy Code

[Serializable]

publicclass 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.

Collapse Copy Code

publicclass POService : IPOService

{

publicevent EventHandler<POStateChangedEventArgs> POCreated;

publicevent EventHandler<POStateChangedEventArgs> POAuthorised;

publicevent EventHandler<POStateChangedEventArgs> POSent;

publicvoid CreatePO(Guid id, PO po)

{

if (POCreated != null)

{

POCreated(null, new POStateChangedEventArgs(id, po));

}

}

publicvoid AuthorisePO(Guid id, PO po)

{

if (POAuthorised != null)

{

POAuthorised(null, new POStateChangedEventArgs(id, po));

}

}

publicvoid 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:

'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.

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.

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'.

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

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.

Collapse Copy Code

privatestaticvoid 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:

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.

A good Amount of examples can be seen in this link

For ASP.net using WWF

Contents

  • Introduction
  • Navigational Logic and Rules
  • Setting Up the Workflow Runtime
  • Navigational Workflow Architecture
  • Creating and Starting a New Navigational Workflow
  • The Previous and Next Events: How Navigation Works
  • Introduction
  • Explanation
  • Synchronization, Self-Healing, and the Browser Back/Forward Buttons
  • Browser Back/Forward Buttons
  • Synchronization and Self-Healing
  • Error Pages
  • Persistence and Long Running Workflows
  • Skipping Between Pages
  • Running the Code
  • History

Introduction

What is a Navigational Workflow? In a nutshell, a Navigational Workflow is a method of describing navigational logic for an application as well as the rules that ensure that logic is adhered to. Any application that must follow some sort of process flow requires a Navigational Workflow.