Click here to Skip to main content
15,867,568 members
Articles / Programming Languages / C#
Article

Windows Workflow Foundation ASP.NET State Machine

Rate me:
Please Sign up or sign in to vote.
4.00/5 (17 votes)
12 May 2006CPOL5 min read 153.9K   1.5K   46   23
Single Page State Machine workflow

Introduction

Windows Workflow Foundation (WF) implements workflow design patterns to solve real-world problems. In particular, State Machine models interaction between an external event and internal state transition,which is very similar to Web Page Flow. In this article, I will build a Web Site Registration System to illustrate how to implement an ASP.NET based WF State Machine. (For additional information, you may read Don Box and Dharma Shukla, Jon Flanders, Dino Esposito.)

Real-World Problem Described

A financial Web site has three types of users trying to register: Casual users without any business relationship, Owners of contracts and agents of owners of contracts. Casual Users directly go to login data entry page while the other two need to go through a validation process before hitting login Data Entry page. If validation fails, the workflow will end. 

ISMConnector -- WF State Machine Connector Interface

State Machine does just one thing: waits for a specific set of events to arrive. When an event does arrive, State Machine will do some back-office processing and move to the next state to wait for another event. So it is not surprising to see the following Interface for communication to a State Machine (SM) with four events and correlations to the next state:

C#
[ExternalDataExchange]
[CorrelationParameter("nextStage")]
public interface ISMConnector
{
    [CorrelationAlias("nextStage", "e.Command")]
    event EventHandler<RegEventArgs> RegTypeSelected;
    [CorrelationAlias("nextStage", "e.Command")]
    event EventHandler<RegEventArgs> AgentInfoSubmitted;
    [CorrelationAlias("nextStage", "e.Command")]
    event EventHandler<RegEventArgs> OwnerInfoSubmitted;
    [CorrelationAlias("nextStage", "e.Command")]
    event EventHandler<RegEventArgs> RegInfoSubmitted;
    [CorrelationInitializer()]
    void SMSnapshot( string nextStage);
}

As always, attributes are hints to compilers to generate extra code or to runtime to setup context. Specifically, [ExternalDataExchange] asks WF tool wca.exe to consume this interface and generate the following activities:

  1. Four eventSinks (HandleExternalEventActivity) for one-way communication into State Machine. Also Registration Data (such as AgentID, SSN, Address) are passed as part of RegEventArgs.
  2. One CallExternalMethodActivity to be inserted just before each event sink to take a snapshot of State Machine.

Note that [CorrelationInitializer()] on SMSnapshot() mandates WF Runtime to set function parameter "nextStage" when executing snapshot activity, while the other correlation related attributes hint the compiler to generate property such as the following:

C#
[System.Workflow.ComponentModel.Compiler.ValidationOptionAttribute(...)]
public string nextStage {
get ..

set..
}


This property maps to EventArgs property "Command".  These "compiler hints" are mostly confusing because they not only generate code but also require correctly setting properties using WF IDE designer in the following section.

Registration State Machine

This State Machine has three states: WaitingRegTypeSelection, WaitingValidationSubmit and WaitingRegInfoSubmit. Each state has one  "SnapShot" Container Activity and one or more event sinks container activities:

RegSMWorkflowDiagram

Each Event sink moves the State Machine into another state by "SetState" Activity after some processing:

AgentInfoSubmitted

If validation fails, "Error Out" activity will be dynamically inserted and the State Machine will end:

C#
WorkflowChanges wfc = new WorkflowChanges(this);
StateActivity sa = wfc.TransientWorkflow.Activities["WaitingRegInfoSubmit"] 
				as StateActivity;
SetStateActivity ssa = new SetStateActivity();
ssa.TargetStateName = "ErrorOut";
   `(sa.Activities["initializeWaitingRegInfoSubmit"] 
		as StateInitializationActivity).Activities.Insert(0, ssa);
ValidationErrorCollection err = wfc.Validate();
this.ApplyWorkflowChanges(wfc);

Note that properties on activities such as "nextStage", "Command" must match the intention of correlation attributes by setting itself to RegistrationMachine.NextStage property through IDE property grid. Each setting is scoped to the state hosting the activity by choosing "Correlation Token" to be owned by a corresponding state through IDE property grid as well.

Up to this point, we merely set up objects, component and attributes through IDE designer and code generation tools. To load these objects into memory for execution, we need to set up WF runtime environment and services.

WF Runtime vs. WF Instance

WinFx requires that each AppDomain can have only one WF runtime. This requirement is easy to meet for Windows Forms or Console Application, since one user interacts with just one AppDomain and a different user interacts with a different AppDomain; by embedding one WF runtime in the unique AppDomain, all workflows will be isolated.

For ASP.NET Web site or Web application, we no longer can create one AppDomain per user or per page request since all pages mostly share an AppDomain and therefore a single WF runtime. Specifically, we write the following HttpModule to create a single WF runtime for all workflows in the Web site /Web application to use:

C#
public class WorkflowHost : IHttpModule
{
    ......
    public static WorkflowRuntime RuntimeWithServices
    {
	get {
	.....
	wr = new WorkflowRuntime();
	wr.AddService(new ManualWorkflowSchedulerService());
	ExternalDataExchangeService de = new ExternalDataExchangeService();
	wr.AddService(de);
	de.AddService(new JQD.LocalService()); 
	wr.StartRuntime();
	.....
    }
    ......
}

But clearly, users still need to isolate their Workflow instance so that State Transition will not trash each other. We accomplish this goal by writing the following code in each page's load event handler:

C#
WorkflowRuntime wr;
WorkflowInstance wi;
ManualWorkflowSchedulerService ms;
   protected void Page_Load(object sender, EventArgs e)
{
    .....
    wr = JQD.WorkflowHost.RuntimeWithServices;
    wi = wr.CreateWorkflow(typeof(RegMachineWorkflow.RegStateMachine));
    ms = wr.GetService<ManualWorkflowSchedulerService>();
    ms.RunWorkflow(wi.InstanceId);
    ...
}

Note that we used ManualWorkflowScheduler to run Workflow instance, rather than directly call Start() method on each WF instance. This will guarantee that different user page requests will be "serialized" by the scheduler  to avoid collision. In this manner, we have achieved isolation of users and page requests even we have just one AppDomain hosting WF Runtime.
 
Finally, I have implemented ISMConnector in class JQD.LocalService and use it to raise event behind page button click event handler and receive "SnapShot" in Page PreRender Event Handler.

About Sample Code Download

You need to install the following to run the sample solution: .NET 2.0, Visual Studio.NET 2005 professional, WinFx runtime and Windows SDK, Orcas WinFx Development Tool, VS.NET Extension for Windows Workflow Foundation.
All these are Feb 2006 CTP and as of May 12, 2006, you can still download parts of the required software here.

The sample solution is made up of one Website (RegWebSite), one WF State Machine Library project (RegMachineWorkflow) and one Class Library Project (LocalService).

SolutionExplorer

LocalService project has a build event to run wca.exe tool and copy generated code to the upper directory. You can  run multiple instances of Browser for registration and they will hit the same WF runtime but different WF instances. And upon completion or error condition, you will return to the first step to try out more. Note that if you hit the back button and try to re-execute the previous step, you will get an error due to "miss-step". I did not handle exception for simplicity.

Conclusion

I demonstrated to you how to model an ASP.NET State Machine by visualizing through the concept of Connector, SnapShot and EventSink. Hopefully, this article will help you to solve real-world problems using WF. 

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Web Developer
United States United States
I am a Microsoft Certified Application Developer (MCAD), currently focusing on using .Net Framework to develop Business Solutions. I am mostly language neutral. I have used C, C++, ATL, MFC, VB.Net, C#, VB 6, PL/SQL, Transact SQL, ASP, Fortran, etc.

Comments and Discussions

 
GeneralWorkflow Application Pin
vkkishore_s17-Mar-09 3:43
vkkishore_s17-Mar-09 3:43 
Questionworkflow persistence store error Pin
Ni Na10-Mar-08 22:50
Ni Na10-Mar-08 22:50 
hi
I am working with a system which uses windows workflow.

I was not part of the initial process so I don't know much about workflow and how it works and how it was designed.

The workflow persistence store error appears often. Up till now all I have been doing is deleting it from the database and letting the user recapture the faulty record. This no longer is working as I spend most of my day storing this issue out.

Where do I go and trace this error and how do I track this error. Also how do I prevent this error from happening again.

Thanks
Confused | :confused:

J9

GeneralRe: workflow persistence store error Pin
jqd200116-Mar-08 6:56
jqd200116-Mar-08 6:56 
QuestionWca.exe Pin
Hardy Wang12-Jun-07 7:27
Hardy Wang12-Jun-07 7:27 
AnswerRe: Wca.exe Pin
jqd200113-Jun-07 9:21
jqd200113-Jun-07 9:21 
GeneralSome post the following but did not show up Pin
jqd200115-Feb-07 12:24
jqd200115-Feb-07 12:24 
GeneralRe: Some post the following but did not show up Pin
jqd200115-Feb-07 12:25
jqd200115-Feb-07 12:25 
GeneralAplication architecture Pin
frun29-Jan-07 2:26
frun29-Jan-07 2:26 
GeneralRe: Aplication architecture Pin
jqd200129-Jan-07 8:37
jqd200129-Jan-07 8:37 
GeneralRe: Aplication architecture Pin
frun30-Jan-07 0:42
frun30-Jan-07 0:42 
GeneralRe: Aplication architecture [modified] Pin
jqd200130-Jan-07 17:43
jqd200130-Jan-07 17:43 
GeneralIs this solution still valid Pin
Dewey21-Jan-07 14:52
Dewey21-Jan-07 14:52 
GeneralRe: Is this solution still valid Pin
jqd200121-Jan-07 17:50
jqd200121-Jan-07 17:50 
GeneralRe: Is this solution still valid Pin
Dewey21-Jan-07 23:06
Dewey21-Jan-07 23:06 
GeneralRe: Is this solution still valid Pin
jqd200125-Jan-07 5:52
jqd200125-Jan-07 5:52 
GeneralClarification -- regarding &quot;Workflow Serialization&quot; [modified] Pin
jqd20012-Jun-06 18:26
jqd20012-Jun-06 18:26 
GeneralWorkflows not serialized Pin
robert.chaplin+junk@gmail.com25-May-06 20:28
robert.chaplin+junk@gmail.com25-May-06 20:28 
GeneralRe: Workflows not serialized [modified] Pin
jqd200126-May-06 17:50
jqd200126-May-06 17:50 
GeneralRe: Workflows not serialized [modified] Pin
robert.chaplin+junk@gmail.com1-Jun-06 13:01
robert.chaplin+junk@gmail.com1-Jun-06 13:01 
GeneralRe: Workflows not serialized Pin
luhur24-Aug-07 3:50
luhur24-Aug-07 3:50 
GeneralGain runtime error Pin
ruddy.tw18-May-06 23:09
ruddy.tw18-May-06 23:09 
GeneralRe: Gain runtime error Pin
jqd200120-May-06 15:11
jqd200120-May-06 15:11 
GeneralRe: Gain runtime error Pin
4th1-Jun-06 4:00
4th1-Jun-06 4:00 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.