Jon Flanders' Blog

Using WF to run a page-flow

Friday, March 17, 2006 5:45:42 PM (GMT Standard Time, UTC+00:00)

There are many really great things about using workflow as the logic behind your application.  The one that really excites me is the visibility workflow gives you into what your application is doing.

 

Another great thing is that many types of applications are naturally workflows, and so using workflow for those applications is really a no-brainer.  One of the most-often cited examples is page flow in ASP.NET.   Moving a user from page to page in an ASP.NET application is really a workflow.

 

Now, until the ASP.NET team and WF team come out with something official in terms of integration, using WF as page-flow is going to be kind of ad hoc.  Here is my take on one way to do this.

 

Here are the goals I had in mind when I built this sample (which is based partly on other applications I have built already using WF and ASP.NET).

 

1)   The WF and the ASP.NET Pages should be unaware of each other.  ASP.NET shouldn’t know it is being run by a workflow, and the workflow shouldn’t know it is being called from ASP.NET.

 

2)  The ASP.NET model should be preserved.  What I mean by this is that ASP.NET pages should be written using a control-based, data-bound methodology.  I also am a big fan of ASP.NET 2.0 and all the work they have done to make building pages easier.  Some other examples of using WF with ASP.NET I’ve seen make the pages go out of the regular model.  IMO – there is a reason for the page model in ASP.NET and I wanted to try to preserve that model in my integration.  So this means using server-side controls and events, along with data-binding.

 

Here is an overview of this sample.  First – there is a state-machine based workflow.  Although a sequential workflow would work, state-machine is really more appropriate for this scenario (see Dave Green’s post here about the two different models).

 

The workflow takes as parameters an OrderedDictionary and a string.  The OrderedDictionary holds onto the real parameters.   The reason I use an OrderedDictionary is to make the ASP.NET model easier to use.  With declarative data sources in ASP.NET 2.0, generally the data source control takes care of processing the form data (and other data, like sessions or cookies) by using a ParameterCollection.  The ParameterCollection is made up of Parameter objects which are responsible for extracting data from controls, the session object, or other objects.   The data source control can then use this to actual perform CRUD operations.   The idea is that the page can be written using the normal model – and then the parameters are gathered by the data source control, and rather than have the parameters processed by the data source, the parameters are passed into the workflow.   My concept here is to let ASP.NET developers write pages using their regular model and then integrate with WF seamlessly.

 

So this could be done by using an existing datasource and extracting the parameters:

 

base._params = SqlDataSource1.SelectParameters.GetValues(HttpContext.Current, SqlDataSource1) as OrderedDictionary;

 

Or by manually packing up parameters into an OrderDictionary instance:

 

//manually create the parameters
this.Parameters = new OrderedDictionary();
this.Parameters.Add(TextBox1.ID, TextBox1.Text);
this.Parameters.Add(TextBox2.ID, TextBox2.Text);

  

The other parameter to the workflow is just a string.  This is what I call the “command”.   The concept is that the page will pass a “command” to the workflow, and then based on the way the workflow is written, the workflow will pass back to the page the next “command”.  Based on the next command value, the integration layer between the pages and the workflow will redirect to the next page.  The next page value is also based on configuration.   The idea is that the configuration mapping will control what page is next in the flow, and that the workflow will just be passing back the next “command” which will then be mapped in configuration to a page.   This keeps the workflow from knowing it is running a page flow and the page from knowing it is using a workflow.

 

After the workflow starts, it continues to live (placed in ASP.NET session) and then is invoked for the rest of the page flow.  The workflow instance then waits for communication from the host (which is really implemented in a single class called WorkflowWrapper).  These events (a custom activity based on HandleExternalEvent – generated using the wca.exe tool) also take an OrderedDictionary and a string.  The events also use correlation; this is why the external data exchange interface only needs on event.

 

 

The CallExternalMethod activity passes back to the host (through the local service implementation) a DataSet, and the “next” command.  The next command is used to redirect based on the configuration, and the DataSet is placed in ASP.NET Session.   When the next page is executed (after the redirect) the page can extract the DataSet from ASP.NET Session (all the operations of each page is actually wrapped in a BasePage class implementation).

 

 

The other interesting thing about each ASP.NET page is that none of them directly calls the workflow or handle control events.  The base page class does this.  Rather than limit the kind of controls that each derived class can use, the base class overrides OnBubbleEvent, and then just extracts the control as an IButtonControl, which means this will work with Button, LinkButton, or ImageButton (to handle other kinds of controls that post-back this method would have to be modified).

 

The local service and the event activities are separated into a separate library – as is the workflow.  To make this same example work with say a set of Windows Forms – there is just one line of code in the local service implementation that would need to be changed (the line that stores the result of the CallExternalMethod in the HttpContext.Items collection).

 

In this example – Default.aspx is the first page in the flow.  It has two text boxes, the values of which get passed to the workflow.  The next page is Page2.aspx and it gets two DataTables from the DataSet and uses it to bind a GridView and a DropDownList.    The rest of the pages just flow – there isn’t any really functionality going on with them.  If you go into the web.config – you can move pages around in the configuration and make the flow of the pages different.

 

I probably will have more to write about on this topic – so this is just the first post.  I am interested in ideas of comments anyone has on this topic, so feel free to critique my design – as I said this is just my beginning thoughts on how to use WF with ASP.NET – my next task is to create a custom datasource control for WF specifically.

aspnetwfpageflowexample.zip (127.88 KB)

ASP.NET 2.0 | WF   #    Comments [13]   Tracked by:
"ASP.NET/WF Integration" (Ryan McIntyre) [Trackback]
"User Interface Page Flow with WF" (Paul Andrew) [Trackback]
http://veda.parivedasolutions.com/blogs/brianorrell/archive/2006/03/29/30.aspx [Pingback]
"Windows Workflow Foundation mit ASP.NET nutzen" (Alex on ASP.NET) [Trackback]
http://blogs.dotnetgerman.com/alexonasp.net/PermaLink,guid,70f9fa35-2821-49c3-87... [Pingback]
"Jon Flanders on Windows Workflow Foundation and ASP.NET" (Kirk Allen Evans' Blo... [Trackback]
"Coming improvements to ASP.NET hosting of Windows Workflow Foundation after WF ... [Trackback]
http://blogs.msdn.com/pandrew/archive/2006/04/28/586181.aspx [Pingback]
"Windows Workflow Foundation Resource List" (MarcosT Blog) [Trackback]
"Windows Workflow Foundation Resource List" (MarcosT Blog) [Trackback]
"Windows Workflow Foundation Resource List" (MarcosT Blog) [Trackback]
"Utilisez Windows Workflow Foundation avec ASP.NET" (RedoBlog - The .NET Gentlem... [Trackback]
"WF PageFlow Cleverness" (the roarty blog) [Trackback]
"Reactive Web Development versus Continuations" (K. Scott Allen) [Trackback]
"Talk Resources: WF & ASP.NET" (this.Pose() as Expert) [Trackback]
http://chrison.net/TalkResourcesWFASPNET.aspx [Pingback]
"maryland automotive windshield" (maryland automotive windshield) [Trackback]
"her first audition" (her first audition) [Trackback]
"annuncio affitti isernia" (annuncio affitti isernia) [Trackback]
"paul posey tallahassee florida" (paul posey tallahassee florida) [Trackback]
"georgia bulldog" (georgia bulldog) [Trackback]
"cd organizers" (cd organizers) [Trackback]
"offerta praga" (offerta praga) [Trackback]
"female body builders posing" (female body builders posing) [Trackback]
"congelatore da incasso" (congelatore da incasso) [Trackback]
"cameriere in autoreggenti" (cameriere in autoreggenti) [Trackback]
"Sports Betting Rss Feed" (Sports Betting Rss Feed) [Trackback]
"albuquerque respiratory jobs va medical center" (albuquerque respiratory jobs v... [Trackback]
"tiaras and headpieces" (tiaras and headpieces) [Trackback]
"used kountry aire camper trailer" (used kountry aire camper trailer) [Trackback]
"columbus ohio commercial real estate" (columbus ohio commercial real estate) [Trackback]
"invisibile pulcino dildo" (invisibile pulcino dildo) [Trackback]
"sms gratis cellulare" (sms gratis cellulare) [Trackback]
"racconto erotici con animali" (racconto erotici con animali) [Trackback]
"zyban side affects" (zyban side affects) [Trackback]
"downlineincome.com" (downlineincome.com) [Trackback]
"www.pokerplayersusa.com" (www.pokerplayersusa.com) [Trackback]
"www.mommyco.com" (www.mommyco.com) [Trackback]
"www.cannabisvaporizers.com" (www.cannabisvaporizers.com) [Trackback]
"www.feminizedmarijuanaseeds.com" (www.feminizedmarijuanaseeds.com) [Trackback]
"www.bewbs.com" (www.bewbs.com) [Trackback]
"hackgs.com" (hackgs.com) [Trackback]
"www.neptunesbeachclub.com" (www.neptunesbeachclub.com) [Trackback]
"www.conferencecalldirectory.net" (www.conferencecalldirectory.net) [Trackback]
"www.ringtone-center.com" (www.ringtone-center.com) [Trackback]
"www.herbalmarijuanavaporizer.com" (www.herbalmarijuanavaporizer.com) [Trackback]
"www.herbvaporizers.com" (www.herbvaporizers.com) [Trackback]
"www.marijuanavapor.com" (www.marijuanavapor.com) [Trackback]
"www.jntah.com" (www.jntah.com) [Trackback]

Friday, March 17, 2006 6:53:00 PM (GMT Standard Time, UTC+00:00)
This looks really interesting, but I just tried to build it with WinWF Beta 2.2, and it doesn't build :-(

It says:

Error 1 'PageFlowLibrary.Workflow1.OnInitializeForRuntime()': no suitable method found to override c:\Program Files\Microsoft SDKs\Windows Workflow Foundation\Jon Flanders\aspnetwfpageflowexample\Projects\ASPNETWFPageFlow\PageFlowLibrary\Workflow1.cs Line 28 Column 27

and:

Error 2 'PageFlowLibrary.Workflow1.OnInitializeForRuntime()' is a new virtual member in sealed class 'PageFlowLibrary.Workflow1' c:\Program Files\Microsoft SDKs\Windows Workflow Foundation\Jon Flanders\aspnetwfpageflowexample\Projects\ASPNETWFPageFlow\PageFlowLibrary\Workflow1.cs Line 28 Column 27

Any idea why this is?

Thanks.

Regards,

Trevor
Friday, March 17, 2006 6:55:13 PM (GMT Standard Time, UTC+00:00)
Because the sample in based on Beta 2. I am still using the Feb WinFX CTP to get the indigo bits as well.
Friday, March 17, 2006 6:58:41 PM (GMT Standard Time, UTC+00:00)
Looks like you need to change that to InitializeProperties(). If that is the only 2.0 2.2 change this sample needs that's pretty cool :)
Sunday, March 19, 2006 10:58:53 AM (GMT Standard Time, UTC+00:00)
Thanks for the hint. I also had to change _ds in workflow1 from private to public, then everything worked fine.

Thanks again.

Regards,

Trevor
Monday, April 03, 2006 9:41:50 AM (GMT Daylight Time, UTC+01:00)
First of all your implementation is really cool.
Many thanks for sharing it with us.
Are you planning to provide an implementation for SQL server persistence (If you will, I will wait and not do it myself :) ).
If the session is removed what will happen ? If the user comes back to the site, will it be possible to pick up the workflow from where it is left ?
batman
Tuesday, April 11, 2006 4:37:43 PM (GMT Daylight Time, UTC+01:00)
I tried running 2 simultaneous pages with this and I received the fllowing error:

Server Error in '/ASPNETWFPageFlow' Application.
--------------------------------------------------------------------------------

Event Queue operation failed with MessageQueueErrorCode QueueNotFound for queue 'Message Properties
Interface Type:PageFlowLibrary.IPageFlow
Method Name:Process
CorrelationValues:
Start
'.
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.

Exception Details: System.InvalidOperationException: Event Queue operation failed with MessageQueueErrorCode QueueNotFound for queue 'Message Properties
Interface Type:PageFlowLibrary.IPageFlow
Method Name:Process
CorrelationValues:
Start
'.

Source Error:


Line 20: if (Process != null)
Line 21: {
Line 22: Process.Invoke(null, e);
Line 23: }
Line 24:


Source File: C:\Documents and Settings\XXX\Desktop\aspnetwfpageflowexample\aspnetwfpageflowexample\Projects\ASPNETWFPageFlow\PageFlowService\LocalService.cs Line: 22

Stack Trace:


[InvalidOperationException: Event Queue operation failed with MessageQueueErrorCode QueueNotFound for queue 'Message Properties
Interface Type:PageFlowLibrary.IPageFlow
Method Name:Process
CorrelationValues:
Start
'.]
System.Workflow.Runtime.WorkflowQueuingService.GetQueue(IComparable queueID) +548208
System.Workflow.Runtime.WorkflowQueuingService.EnqueueEvent(IComparable queueName, Object item) +62
System.Workflow.Runtime.WorkflowExecutor.EnqueueItem(IComparable queueName, Object item, IPendingWork pendingWork, Object workItem) +184
System.Workflow.Runtime.WorkflowInstance.EnqueueItem(IComparable queueName, Object item, IPendingWork pendingWork, Object workItem) +101
System.Workflow.Activities.WorkflowMessageEventHandler.EventHandler(Object sender, ExternalDataEventArgs eventArgs) +418

[EventDeliveryFailedException: Event "Process" on interface type "PageFlowLibrary.IPageFlow" for instance id "5830cd1d-6469-456c-b7ca-25675a53708e" cannot be delivered.]
System.Workflow.Activities.WorkflowMessageEventHandler.EventHandler(Object sender, ExternalDataEventArgs eventArgs) +691
PageFlowLibrary.PageFlowLocalService.Invoke(PageFlowEventArgs e) in C:\Documents and Settings\XXX\Desktop\aspnetwfpageflowexample\aspnetwfpageflowexample\Projects\ASPNETWFPageFlow\PageFlowService\LocalService.cs:22
ASPNETWFPageFlow.WorkflowWrapper.InternalSend(String oe, OrderedDictionary prms) in c:\Documents and Settings\XXX\Desktop\aspnetwfpageflowexample\aspnetwfpageflowexample\ASPNETWFPageFlow\App_Code\Module.cs:163
ASPNETWFPageFlow.WorkflowWrapper.Process(String command, String arg, OrderedDictionary prms) in c:\Documents and Settings\XXX\Desktop\aspnetwfpageflowexample\aspnetwfpageflowexample\ASPNETWFPageFlow\App_Code\Module.cs:145
BasePage.OnBubbleEvent(Object source, EventArgs args) in c:\Documents and Settings\XXX\Desktop\aspnetwfpageflowexample\aspnetwfpageflowexample\ASPNETWFPageFlow\App_Code\BasePage.cs:44
System.Web.UI.Control.RaiseBubbleEvent(Object source, EventArgs args) +35
System.Web.UI.WebControls.Button.OnCommand(CommandEventArgs e) +115
System.Web.UI.WebControls.Button.RaisePostBackEvent(String eventArgument) +163
System.Web.UI.WebControls.Button.System.Web.UI.IPostBackEventHandler.RaisePostBackEvent(String eventArgument) +7
System.Web.UI.Page.RaisePostBackEvent(IPostBackEventHandler sourceControl, String eventArgument) +11
System.Web.UI.Page.RaisePostBackEvent(NameValueCollection postData) +33
System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +5102

Any ideas?

Thanks.
Tuesday, May 02, 2006 1:24:37 PM (GMT Daylight Time, UTC+01:00)
great notings about WF.i also have few queries about workflow but those which are created using biztalk:

1.Is it able to use .net classes created using VS.net as activities inside workflow.

2. Can we invoke workflow created in it being called from .net assembly.Here i wish to ask about suppose we create a workflow in biztalk. save it as "a.someextension". Now can i reference it as a reference in .net assembly.I mean to ask here that what are different type of files(type of extensions) in which workflow generated in biztalk can be saved. Are they saved as .dlls.As per my understanding, biztalk workflows are available as webservice to be used inside .net application.

3. Are the workflows created in biztalk available as a webservice

4. Does it have logging support and reporting features for workflows.

5. Is transaction and entire rollback supported throughout the workflow.

6. Does it support multiple versions of the same workflow running simultaneously.Here i want to know if suppose we create a new version of a workflow in biztalk while keeping the older one intact.So now can i invoke both the versions of the same workflow at the same time from different applications(suppose .net applications).


7. Does it support recovery from abrupt breakdown. If yes, what is the features of its Error handling.Here i need to know whether biztalk workflow provides rollback at activitiy and process(group of activities) level

Kindly provide me with your inputs on the above.It would be very much beneficial for my research
Monday, June 12, 2006 7:26:40 AM (GMT Daylight Time, UTC+01:00)
This is great. Thanks for the work. I have a concern, though:

Once you hit the browser's back button the whole thing falls apart because the workflow gets out-of-sync with the page.

1. Click "Page One" button.
2. Click browser back button.
3. Click "Page One" button again.
4. Get error: Event "Process" on interface type "PageFlowLibrary.IPageFlow" for instance id "f33c5755-c6a6-4bf8-91fb-1028f15732d8" cannot be delivered.

You'll hit the same issue if the user opens more than one browser window (as another poster pointed out).

So, keeping the workflow alive in session is clearly not a sound approach. You almost need to persist a copy of the workflow with each page render (maybe into view state or something) then force the workflow to unload. You can then re-hydrate the workflow with each postback. Then if the user clicks the back button he gets a point-in-time copy of the workflow that matches the page in his browser.

I wonder what the performance implications of loading/unloading serializing/deserializing a workflow with each postback would be? But I think it would solve the problem.
Saturday, July 08, 2006 11:33:49 PM (GMT Daylight Time, UTC+01:00)
Hello,
Thanks for the great example! I'm just learning .NET after using Java for 5+ years. I'm receiving a runtime exception with the 2.2 framework.

Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code. Any thoughts.

Exception Details: System.Workflow.ComponentModel.Compiler.WorkflowValidationFailedException: The workflow failed validation.

Source Error:

Line 114: wfprms.Add("InitialCommand", oe);
Line 115: wfprms.Add("InitialParams", prms);
Line 116: WorkflowInstance wi = wr.CreateWorkflow(Type.GetType(_config.Type),wfprms);
Line 117: wi.Start();


Source File: c:\Documents and Settings\ninet\Desktop\aspnetwfpageflowexample\ASPNETWFPageFlow\App_Code\Module.cs Line: 116

Stack Trace:

[WorkflowValidationFailedException: The workflow failed validation.]
System.Workflow.Runtime.WorkflowDefinitionDispenser.ValidateDefinition(Activity root, Boolean isNewType, ITypeProvider typeProvider) +927
System.Workflow.Runtime.WorkflowDefinitionDispenser.LoadRootActivity(Type workflowType, Boolean createDefinition, Boolean initForRuntime) +74
System.Workflow.Runtime.WorkflowDefinitionDispenser.GetRootActivity(Type workflowType, Boolean createNew, Boolean initForRuntime) +221
System.Workflow.Runtime.WorkflowRuntime.InitializeExecutor(Guid instanceId, CreationContext context, WorkflowExecutor executor, WorkflowInstance workflowInstance) +69
System.Workflow.Runtime.WorkflowRuntime.Load(Guid key, CreationContext context, WorkflowInstance workflowInstance) +260
System.Workflow.Runtime.WorkflowRuntime.GetWorkflowExecutor(Guid instanceId, CreationContext context) +489
System.Workflow.Runtime.WorkflowRuntime.InternalCreateWorkflow(CreationContext context, Guid instanceId) +46
System.Workflow.Runtime.WorkflowRuntime.CreateWorkflow(Type workflowType, Dictionary`2 namedArgumentValues, Guid instanceId) +84
System.Workflow.Runtime.WorkflowRuntime.CreateWorkflow(Type workflowType, Dictionary`2 namedArgumentValues) +45
ASPNETWFPageFlow.WorkflowWrapper.StartWorkflow(String url, String oe, OrderedDictionary prms) in c:\Documents and Settings\ninet\Desktop\aspnetwfpageflowexample\ASPNETWFPageFlow\App_Code\Module.cs:116
ASPNETWFPageFlow.WorkflowWrapper.InternalSend(String oe, OrderedDictionary prms) in c:\Documents and Settings\ninet\Desktop\aspnetwfpageflowexample\ASPNETWFPageFlow\App_Code\Module.cs:154
ASPNETWFPageFlow.WorkflowWrapper.Process(String command, String arg, OrderedDictionary prms) in c:\Documents and Settings\ninet\Desktop\aspnetwfpageflowexample\ASPNETWFPageFlow\App_Code\Module.cs:145
BasePage.OnBubbleEvent(Object source, EventArgs args) in c:\Documents and Settings\ninet\Desktop\aspnetwfpageflowexample\ASPNETWFPageFlow\App_Code\BasePage.cs:44
System.Web.UI.Control.RaiseBubbleEvent(Object source, EventArgs args) +35
System.Web.UI.WebControls.Button.OnCommand(CommandEventArgs e) +86
System.Web.UI.WebControls.Button.RaisePostBackEvent(String eventArgument) +155
System.Web.UI.WebControls.Button.System.Web.UI.IPostBackEventHandler.RaisePostBackEvent(String eventArgument) +7
System.Web.UI.Page.RaisePostBackEvent(IPostBackEventHandler sourceControl, String eventArgument) +11
System.Web.UI.Page.RaisePostBackEvent(NameValueCollection postData) +33
System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +4919
Monday, July 10, 2006 12:29:05 PM (GMT Daylight Time, UTC+01:00)
Hi,

What are this 'IPageFlow.Invokes.cs' and 'IPageFlow.Sinks.cs' files?

Thanks
Tuesday, December 19, 2006 4:54:41 PM (GMT Standard Time, UTC+00:00)
Is the following scenario feasible for implementing the PageFlow:

1. Have just one instance of the Workflow alive and shared among all users connecting through the web.
2. initialize the state of the Workflow to a given state prior to processing the event.

this is what I what the application to do: On receiving a post back read the CurrentState and Event from some hidden variables (will be set using javascript), then using these two pieces of information initialize the workflow to the CurrentState and then process the Event.

Will using a singleton workflow result in some unnecessary contention?

Thanks!
Wednesday, December 20, 2006 5:45:41 AM (GMT Standard Time, UTC+00:00)
Can we have conditional navigation
For example:
Page 2 to Page 3 if condition x is false
Page 2 to Page 4 i.e skip Page 3 if condition x is true.
How do you tackle this scenario.
Wednesday, January 17, 2007 11:23:07 AM (GMT Standard Time, UTC+00:00)
http://www.masteringbiztalk.com/blogs/jon/CommentView,guid,0fae3e73-7801-4cf6-a5a5-9370b0f99973.aspx
All comments require the approval of the site owner before being displayed.
Name
E-mail
Home page

Comment (HTML not allowed)  

Enter the code shown (prevents robots):

Live Comment Preview