Windows Workflow Foundation 4 and persistence

Note: This blog post is written using the .NET framework 4.0 Beta 2

The whole persistence model has changed quite a but for WF4.

 

The persistence class out of the box is called SqlWorkflowInstanceStore and as the name suggests it saves workflow data in either SQL Server 2005 or 2008. It is based on the InstanceStore class to if you prefer some other store all you need to do is subclass the InstanceStore and create your own.

 

So what can we do with the SqlWorkflowInstanceStore?

We can attach it to either a WorkflowApplication or a WorkflowServiceHost and persist workflows when we want. Notice I left out the WorkflowInvoker. This can be used to run only short lived workflows and doesn’t support persistence.

The case of the WorkflowApplication is quite simple.

var instanceStore = new SqlWorkflowInstanceStore(connStr);


WorkflowApplication app = new WorkflowApplication(workflow);


app.InstanceStore = instanceStore;


app.Run();

Okay its a little more involved then that.

First of all you need to create a database to store the workflow state in. There are a couple of SQL files in the “C:\Windows\Microsoft.NET\Framework\v4.0.21006\SQL\en” folder. The two you need are SqlWorkflowInstanceStoreSchema.sql and SqlWorkflowInstanceStoreLogic.sql. I created a batch file to quickly recreate my persistence database like this:

osql -E -S .\sqlexpress -Q "Drop Database WorkflowInstanceStore"


osql -E -S .\sqlexpress -Q "Create Database WorkflowInstanceStore"


osql -E -S .\sqlexpress -d WorkflowInstanceStore -i SqlWorkflowInstanceStoreSchema.sql


osql -E -S .\sqlexpress -d WorkflowInstanceStore -i SqlWorkflowInstanceStoreLogic.sql

 

So with all this in place we still need to tell the WorkflowApplication when to persist the workflow. There are several ways to to this but one is to use the PersistableIdle callback. This will only fire when an InstanceStore has been provided. Options are to persist and unload the workflow, just unload the workflow or do nothing at all. The callback is just another function so you can make whatever decisions you like.

app.PersistableIdle = e => PersistableIdleAction.Persist;

Another option is to use the Persist activity. This will allow the workflow to decide on extra persistence points regardless of the workflow being idle.

 

How about the WorkflowServiceHost?

With the WorkflowServiceHost we get a couple of different choices. First of all we can just create a SqlWorkflowInstanceStore and set it like this:

var workflow = new Workflow1();


var baseAddress = new Uri("http://localhost:8080/MyWorkflow");


var host = new WorkflowServiceHost(workflow, baseAddress);


 


var connStr = @"Data Source=.\sqlexpress;Initial Catalog=WorkflowInstanceStore;Integrated Security=True;Pooling=False";


var instanceStore = new SqlWorkflowInstanceStore(connStr);


host.DurableInstancingOptions.InstanceStore = instanceStore;


 


host.Open();


 


Console.WriteLine("Listening...");


Console.ReadLine();


host.Close();

Simple but it doesn’t give us any control over when workflows are persisted. We don’t get quite the same control as with a WorkflowApplication in this case, all we can do is set a few timeout values using the WorkflowIdleBehavior like this:

var workflowIdleBehavior = new WorkflowIdleBehavior();


workflowIdleBehavior.TimeToPersist = TimeSpan.FromSeconds(10);


workflowIdleBehavior.TimeToUnload = TimeSpan.FromMinutes(1);


host.Description.Behaviors.Add(workflowIdleBehavior);

We can’t make any decisions based, just set time the workflow is idle before it is persisted and the same before it is unloaded.

 

Suppose we want some more control over the way the SqlWorkflowInstanceStore behaves. We can using the SqlWorkflowInstanceStoreBehavior. This is actually the same class as is used through the config file.

var connStr = @"Data Source=.\sqlexpress;Initial Catalog=WorkflowInstanceStore;Integrated Security=True;Pooling=False";


var behavior = new SqlWorkflowInstanceStoreBehavior(connStr);


behavior.InstanceCompletionAction = InstanceCompletionAction.DeleteNothing;


behavior.InstanceLockedExceptionAction = InstanceLockedExceptionAction.AggressiveRetry;


behavior.InstanceEncodingOption = InstanceEncodingOption.None;


host.Description.Behaviors.Add(behavior);

 
 

So that is all there is to it?

 
Not quite there is a little complexity when it comes to using the same SqlWorkflowInstanceStore with multiple WorkflowApplication instances. Note that the WorkflowServiceHost automatically takes care of this so no need to worry about that.
 
By default a SqlWorkflowInstanceStore will only work with a single WorkflowApplication. If you try to use it with mutiple workflow you can get a InstancePersistenceCommandException with the following message:
SqlWorkflowInstanceStore does not support creating more than one lock owner concurrently. Consider setting InstanceStore.DefaultInstanceOwner to share the store among many applications.
The trick is to set the DefaultInstanceOwner of the SqlWorkflowInstanceStore. The code isn’t hard but not exactly obvious either.
var instanceStore = new SqlWorkflowInstanceStore(connStr);


 


var instanceHandle = instanceStore.CreateInstanceHandle();


var createOwnerCmd = new CreateWorkflowOwnerCommand();


var view = instanceStore.Execute(instanceHandle, createOwnerCmd, TimeSpan.FromSeconds(30));


instanceStore.DefaultInstanceOwner = view.InstanceOwner;


 


// Do whatever needs to be dome with multiple WorkflowApplications


 


var deleteOwnerCmd = new DeleteWorkflowOwnerCommand();


instanceStore.Execute(instanceHandle, deleteOwnerCmd, TimeSpan.FromSeconds(30));



 


The key is the CreateWorkflowOwnerCommand that needs to be executed at the start. And when you use the CreateWorkflowOwnerCommand just make sure not to forget the DeleteWorkflowOwnerCommand otherwise all workflow will remain locked by the owner and can’t be reloaded by another SqlWorkflowInstanceStore


 



Quite a but more flexible than we had before but, specially with the WorkflowApplication, also quite a bit more work and not always as obvious as I would like it.



 



Enjoy!



[f1]
[f2]

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>