Thursday, September 20, 2007

Windows Workflow Foundation State Machine Sample


For details :

Click Here

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:
http://wf.netfx3.com/
http://msdn2.microsoft.com/en-us/netframework/aa663322.aspx

Background

In order to create WF applications,you need to install some required packages/tool.
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 to 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 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.
using System;
using System.Collections.Generic;
using System.Text;
using System.Workflow.Activities;

namespace FirstStateMachineWorkflow
{
[ExternalDataExchange]
public interface ILocker
{
event EventHandler Lock;
event EventHandler 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 below to the class.
Collapse
using System;
using System.Collections.Generic;
using System.Text;
using System.Workflow.Activities;

namespace FirstStateMachineWorkflow
{
[Serializable]
public class Locker : ILocker
{
public event EventHandler Lock;
public event EventHandler Unlock;

public bool lockerState = false;

public bool LockIt(Guid InstanceID)
{
  if (this.Lock != null)
  {
      this.Lock(this,
          new System.Workflow.Activities.ExternalDataEventArgs(InstanceID));
      return true;
  }
  else
  {
      return false;
  }
}

public bool UnlockIt(Guid InstanceID)
{
  if (this.Unlock != null)
  {
      this.Unlock(this,
          new System.Workflow.Activities.ExternalDataEventArgs(InstanceID));
      return false;
  }
  else
  {
      return true;
  }
}
}
}
As you see 'Locker' class uses events to change the workflows state. These 2 methods ('LockIt' and 'UnlockIt') are going to used by 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 as 'HandleLockEvent'. This will hepl us to handle event fired by host application. While 'HandleLockEvent' is selected, in the properties window, click '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. As far as now, we made the 'Lock' event be handled by this handler. But after handling we want to write a message to the console 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:
private void 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) can not run by themselves. They need an external host application. The application may be 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 As StartUp 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 below to run the workflow:
Collapse
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
{
static void Main(string[] args)
{
  WorkflowRuntime workflowruntime = new WorkflowRuntime();
  Locker locker = new Locker();
  Guid locker_ID = Guid.NewGuid();
  Dictionary Params = new Dictionary();
  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 paramaters to workflow. However, we don't have any parameters in our example.
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 '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 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.