Integrating a persisted WF4.0 Workflow with MVC

Introduction

Writing this as lately I’ve been interested in WF4.0 (it’s always pronounced “dub-F” in my head) and saw a post about using WF with MVC to handle a site registration wizard. The tutorial it linked to used a state machine (WF only supports Sequential OOB) workflow without persistence, which made me think that you could easily use a normal workflow with persistence to do a lot of other things inside a web application. WF which is part of .NET4.0 allows you to easily create and call workflow processes, small or large, from anywhere inside .NET code and it’s been drastically improved since .NET3.5. That ease makes it practical to use workflow in places which it was previously too complicated to integrate, places where we normally manually code a process into business or web logic making it difficult to maintain and add standard features like tracing/auditing.

To run the sample or try this yourself you will need .NET4.0 and Visual Studio 2010, the instructions below are a simple overview and do not include all code or references.

1. Creating the WF SQL Persistence store

WF4.0 supports using a default SQL Persistence store, using a DB created using scripts in the .NET4.0 framework directory. Simply create a new DB (call it whatever you like) on your server and run the following scripts:

  1. C:WindowsMicrosoft.NETFrameworkv4.0.21006SQLenSqlWorkflowInstanceStoreSchema.sql
  2. C:WindowsMicrosoft.NETFrameworkv4.0.21006SQLenSqlWorkflowInstanceStoreLogic.sql

Multiple workflows can share the same DB or you can separate them out if you want.

2. Creating the MVC site and Workflow Activity Library

Solution

In Visual Studio, create a new MVC Web Application and add another project of type “Activity Library” (under templates Workflow) to hold your workflow. I also added a console application to test the workflow independently but this is not necessary.

3. Creating the Workflow

Workflow_logic
Wizardstep

Delete the existing workflow activity and create a new one with the name WizardActivity, in the activity drag and drop a Flowchart from the toolbox onto the workflow (using this instead of a state machine) and add a variable “Outcome” to the Flow chart scope (used to store the outcome Next/Back of the steps). In the Activtiy project add a new Class, WizardStep, which will be generic custom activity used for all the steps in the workflow. This activity contains the bookmark code which is used to halt and persist the workflow at the steps. Add the code and compile the project, this will update the workflow toolbox adding WizardStep and allowing you to drag and drop it onto the workflow. Add three Wizard steps to the workflow, setting the Name to “StepN“, Bookmark Name to “StepN” and Input to “Outcome” (this is an output parameter which will set Outcome to the value supplied when resuming the Bookmark in the custom activity). Add a custom Complete activity (which does nothing) and link up the workflow using Decision controls (set condition to “Outcome.Equals(“Next”)”) and arrows as per image. Lastly add a new Class WizardHostHelper (copy code from sample), which acts as the host for the workflow; creating the Workflow application object, setting instance store values, starting/resuming the workflow, actioning the bookmarks.

4. Update the MVC application to use the Workflow

Now to integrate the workflow into the website.

  1. Add a new class WizardModels, to hold the model logic for the wizard steps and workflow service (wrapper to WizardHostHelper).
  2. Add a new controller WizardController, for controller logic used in the wizard.
  3. Add four new views under Wizard, Step1-3 and final, for the views used in the Wizard.
  4. Update AccountModel, add a Guid parameter workflowKey to the MembershipService.CreateUser method, this will store the Workflow Instance key in the standard ASP user security providerUserKey. Also add a method GetUserWorkflowKey to return the key for a username.
  5. Update AccountController, changing Register method to start a workflow instance for the user using the WizardModels.WorkflowService and supply the workflow key into CreateUser. Update both Register and Logon to redirect action to the Wizard index.
  6. Update the web.config to set the instance store connection string.
Step1

And that should be it, registering a new user will now start the workflow process and redirect to the first step. Next/Back buttons on the step views cause the controller to action the steps, progressing the workflow. The workflow is persisted to the DB at each step, so even if the website goes down the current step of the user in the workflow is maintained. The workflow, rather than the controller, decides which step the website should display, separating the process logic from the controller logic.

There is very little code involved, just small service/host classes used to access the workflow and integrate with the website. To add persistence it only really took two lines of code in the host class (setting the workflow application instance store and telling it to persist when idle at the bookmarks), compared to the tutorial sample host. The workflow used could be much more complicated, branching to different steps depending on user response (e.g. redirecting to a different address screen if the user isn’t based in the UK) without adding complexity to the controller logic. As the workflow is separated from the website it can be tested independently (no website needed! automated testing!) and updated without large changes needed in the website. It’s also a very light weight (and cheap) way to add workflow, which is something we may need going forward. I’m currently looking at ways now to use WF with persistence in Azure cloud (without the upcoming Azure AppFabric) for simple workflow solutions, as right now we can’t use our current workflow applications like K2.

Attached sample inside word doc as Posterous won’t accept zips.

MVCWithWF_sample_attached.doc
Advertisements

9 thoughts on “Integrating a persisted WF4.0 Workflow with MVC

  1. Nice post … Hosting WF4 on Azure is not be a problem even without AppFabric composite apps – as long as the workflow instance is more or less idle (persisted) all the time. Then several different workers that are hosting the workflow service can effectively exchange the workflow instance in execution.

  2. @ClemeThanks, I’ve read that the scripts to create the SQL Instance Store schema can be run on SQL Azure with small changes but I’m more interested in using Azure Tables (cheaper, don’t need an active DB instance). To do this I’ll need a custom instance store, trying to make one based on the Xml Persistence Store from the Microsoft WCF/WF samples.

  3. Creating the SQL Azure tables is not a problem, just make sure you are using SQL Management Studio 2008 R2 and select the compatibility with SQL Azure when generating the DB scripts. Another interesting move is to build own Azure table Persistence Store. But Consider that SQL persistence store has more functionality that provided XMLPerisistanceStore in example: Host locking, Runnable instances detection period… When writing your own provider you have to implement that as well, because I believe you will need then in Azure environment. Good luck … share the results.

  4. Hey,i’ve tried your solution, but I have one question: how can I access the data the user entered on each wizard step?Thanks :-)Markus

  5. @MarkusThe workflow is only being used to persist the current step the user is at in the wizard, it isn’t being used to persist the data entered. If you wanted to save the values which the user entered (which the example isn’t doing) you should put the persistence logic in the model and call it in the controller in the post action before calling next/back on the workflow. The example post action isn’t using any of the step models/values, but adding them is basic MVC.

  6. Great work! Just a suggestion … If a user not authenticated starts from the Register link, all works fine. But if an unauthenticated user connects to the site and type by hand the url: http://my_site/Wizard, the cookie with the username has not yet been created and the application reports an error. May be, in the function public ActionResult Index () {..} the variable step of the wizard controller should be set with the url of the Register view… and you should resume workflow only under this condition: if (!string.IsNullOrEmpty(User.Identity.Name)) { }.The best (working) tutorial on WF4 I found so far .

  7. Pingback: Integrating a persisted WF4.0 Workflow with MVC | Sai

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s