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:
- .NET Framework 3.0 Runtime
- Visual Studio 2005 Professional Edition
- Windows Workflow Foundation extensions for Visual Studio 2005
- 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.
using System;
using System.Collections.Generic;
using System.Text;
using System.Workflow.Activities;
namespace FirstStateMachineWorkflow
{
[ExternalDataExchange]
public interface 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:
using System;
using System.Collections.Generic;
using System.Text;
using System.Workflow.Activities;
namespace FirstStateMachineWorkflow
{
[Serializable]
public class Locker : ILocker
{
public event EventHandler<ExternalDataEventArgs> Lock;
public event EventHandler<ExternalDataEventArgs> 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 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:
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) 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 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 given below to run the workflow:
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<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.
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:
- Create a workflow statemachine application.
- Write an Interface for transitions between states (Write events).
- Write the
[Serializable]
class implementing the interface events and calling them
in functions. - Create a host application which uses the class and starts workflow.
History
20th September, 2007: First release