Click here to Skip to main content
Click here to Skip to main content

Windows Workflow Foundation ASP.NET State Machine

, 12 May 2006
Rate this:
Please Sign up or sign in to vote.
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:

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

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

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:

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:

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)

About the Author

jqd2001
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 Pinmembervkkishore_s17-Mar-09 3:43 
Questionworkflow persistence store error PinmemberNi Na10-Mar-08 22:50 
GeneralRe: workflow persistence store error Pinmemberjqd200116-Mar-08 6:56 
QuestionWca.exe PinmemberHardy12-Jun-07 7:27 
AnswerRe: Wca.exe Pinmemberjqd200113-Jun-07 9:21 
GeneralSome post the following but did not show up Pinmemberjqd200115-Feb-07 12:24 
GeneralRe: Some post the following but did not show up Pinmemberjqd200115-Feb-07 12:25 
GeneralAplication architecture Pinmemberfrun29-Jan-07 2:26 
I've a general question about the architecture. Where should be implemented business object having data (e.g. Contract[Id, Signed Date, ...], Customer[Name, Address, Contracts, ...])?
 
What tier should read data from database, list them, assign the status of business object to Workflow state? How to reasign WF state to BO state?
 
I understand, the Win WF is framework to model business processes, but how to work with business data? Do you understand my question?
GeneralRe: Aplication architecture Pinmemberjqd200129-Jan-07 8:37 
GeneralRe: Aplication architecture Pinmemberfrun30-Jan-07 0:42 
GeneralRe: Aplication architecture [modified] Pinmemberjqd200130-Jan-07 17:43 
GeneralIs this solution still valid PinmemberDewey21-Jan-07 14:52 
GeneralRe: Is this solution still valid Pinmemberjqd200121-Jan-07 17:50 
GeneralRe: Is this solution still valid PinmemberDewey21-Jan-07 23:06 
GeneralRe: Is this solution still valid Pinmemberjqd200125-Jan-07 5:52 
GeneralClarification -- regarding &quot;Workflow Serialization&quot; [modified] Pinmemberjqd20012-Jun-06 18:26 
GeneralWorkflows not serialized Pinmemberrobert.chaplin+junk@gmail.com25-May-06 20:28 
GeneralRe: Workflows not serialized [modified] Pinmemberjamesqding26-May-06 17:50 
GeneralRe: Workflows not serialized [modified] Pinmemberrobert.chaplin+junk@gmail.com1-Jun-06 13:01 
GeneralRe: Workflows not serialized Pinmemberluhur24-Aug-07 3:50 
GeneralGain runtime error Pinmemberruddy.tw18-May-06 23:09 
GeneralRe: Gain runtime error Pinmemberjamesqding20-May-06 15:11 
GeneralRe: Gain runtime error Pinmember4th1-Jun-06 4:00 

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

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

| Advertise | Privacy | Mobile
Web02 | 2.8.140721.1 | Last Updated 13 May 2006
Article Copyright 2006 by jqd2001
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid