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?
SqlWorkflowInstanceStore does not support creating more than one lock owner concurrently. Consider setting InstanceStore.DefaultInstanceOwner to share the store among many applications.
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));
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]
I just blogged about this with some additional information which is necessary to get this to work 100%…
http://morganskinner.blogspot.co.uk/2015/01/self-hosting-workflowworking-with.html
Hope that helps someone!