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:
- State:
a situation
- Event:
a stimulus
- 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.
[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.
[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.
[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.
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:
'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.
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:
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:
- Programming Workflow Foundation – Practical WF Techniques and Examples using
XAML and C# by K. Scott Allen
- K. Scott Allen's blog
- Essential Windows Workflow Framework by DharmaShukla, BobSchmidt.
- Official Home of
Workflow Foundation in webspace
- Google! – Search and find more articles/blog posts
appearing in the techie arena everyday.