SqlWorkflowPersistenceService in Windows Workflow Foundation
If you're a big fan of sleeping like me, probably have faced with many cases that your transactions were aborted when you were asleep! For long running transactions this is a common scenario. You can't guarantee the life of a transaction. It can take one second, one minute, one hour or five years or even longer than these.
In Windows Workflow Foundation you should consider all types of transactions so you may get in trouble with a case when your transaction is running for a long time and suddenly aborts for any reason. This can happen for a site workflow which runs for several hours, days or weeks. One day server restarts everything and you'll miss your workflow state.
Thankfully Microsoft thought about these cases and a solution is included in Windows Workflow Foundation to store the state of running workflow and retrieve it later. In this post I'll talk about SqlWorkflowPersistenceService object which does this and stores/retrieves state information in/from SQL Server database.
Write a Simple Workflow
First I start with a simple Sequential Workflow Console Application. It's not more than a simple Delay activity which forces the workflow to wait for 50 milliseconds.
InitializeTimeOutDuration event handler logic for delayActivity1 isn't more than writing a text to Console.
private void delayActivity1_InitializeTimeoutDuration(object sender, EventArgs e)
{
Console.WriteLine("Hello Gholi!");
}
Later I'll use an instance of this workflow for my examples. I could load a workflow on fly for my examples but used this approach to let you see it in designer.
Configure SQL Server Database
In order to be able to save and retrieve the state of workflow runtime using SqlWorkflowPersistenceService, you need to configure your SQL Server database to store data. There are two SQL scripts located at C:\WINDOWS\Microsoft.NET\Framework\v3.0\Windows Workflow Foundation\SQL\EN which are named SqlPersistenceService_Schema.sql and SqlPersistenceService_Logic.sql. First one defines the structure of database and tables and second one defines stored procedures.
Note that DTC must be started for your SQL Server in order to be able to use SqlWorkflowPersistenceService.
I create a database and run these scripts for it to get appropriate structure.
Write a WinFX Windows Application
Now that I produced all prerequisites, I write a simple WinFX application which has two buttons. First one creates a workflow runtime and loads above workflow activity into memory then saves its state into database. Second button retrieves the workflow runtime state from database and follows the activity then unloads it. The XAML definition for user interface isn't necessary here but would be a simple practice for beginners:
<Window x:Class="SqlPersistenceService.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="SqlPersistenceService" Height="250" Width="250"
>
<StackPanel Margin="50">
<Button Click="LoadClick" Width="100" Height="40" Background="Bisque">
Load
</Button>
<Button Click="UnLoadClick" Width="100" Height="40" Background="Chartreuse">
UnLoad
</Button>
</StackPanel>
</Window>
So my user interface is as simple as this:
In the code behind for this user interface I add some references to workflow namespaces and define an id property to keep my workflow instance identifier as well as a string property to keep my database connection string.
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using System.Workflow.Runtime;
using System.Workflow.Runtime.Hosting;
namespace SqlPersistenceService
{
/// <summary>
/// Interaction logic for Window1.xaml
/// </summary>
public partial class Window1 : System.Windows.Window
{
private Guid id;
private string connectionString =
"Data Source=KEYVANNAYYERI;Initial Catalog=SqlPersistenceSample;Integrated Security=True";
There are two click event handlers in this code:
First one (LoadClick) creates a workflow runtime and an instance of SqlWorkflowPersistenceService by passing SQL Server connection string to its constructor. Then adds this object to workflow runtime services. After that, it loads an instance of Workflow1 into workflow runtime then this instance starts and shows its identifier in a MessageBox. This identifier is also stored in id property for later use. Finally workflow runtime stops. At this stage SqlWorkflowPersistenceService stores the state into database.
void LoadClick(object sender, RoutedEventArgs args)
{
WorkflowRuntime workflowRuntime = new WorkflowRuntime();
SqlWorkflowPersistenceService sqlPersistenceService =
new SqlWorkflowPersistenceService(this.connectionString);
workflowRuntime.AddService(sqlPersistenceService);
workflowRuntime.StartRuntime();
WorkflowInstance instance =
workflowRuntime.CreateWorkflow(typeof(SampleWorkflow.Workflow1));
this.id = instance.InstanceId;
MessageBox.Show(instance.InstanceId.ToString(), "Instance GUID");
instance.Load();
instance.Start();
workflowRuntime.StopRuntime();
}
Second click event handler (UnloadClick) loads my workflow runtime from memory again. Then gets a workflow instance of Workflow1 from this workflow runtime by passing previously saved identifier of workflow to GetWorkflow() method. Then shows the identifier of this instance in a MessageBox and finally workflow instance unloads.
Note that you'll see same GUIDs for both MessageBoxes and this means they're both running in same workflow runtime. If you pass any other id to GetWorkflow() method, it will throw an exception because can't find this instance in what is stored in database.
void UnloadClick(object sender, RoutedEventArgs args)
{
WorkflowRuntime workflowRuntime = new WorkflowRuntime();
SqlWorkflowPersistenceService sqlPersistenceService =
new SqlWorkflowPersistenceService(this.connectionString);
workflowRuntime.AddService(sqlPersistenceService);
workflowRuntime.StartRuntime();
WorkflowInstance instance =
workflowRuntime.GetWorkflow(this.id);
MessageBox.Show(instance.InstanceId.ToString(), "Instance GUID");
instance.Unload();
workflowRuntime.StopRuntime();
}
Now I check my database to see the data for my workflow runtime:
You can also configure your applications via configuration files to let them store their workflow runtime state into database.
[advertisement] Axosoft OnTime 2008 is four developer tools in one: bug tracking, project wiki, feature management, and help desk. It manages your development process so developers can focus on coding. Installed or Hosted – Free Single-user license -- Free 30-day team trial.
10 Comments : 10.10.06
Feedbacks
Hi I am very new to WF and I have gone through and read few things about which got me started. I have a situation where users wants some changes to the application we have developed.At the moment they have to fill up the change request forms, get authorisation blah blah.I was hoping that I can automate this by a workflow.Which is Ok but what I would like to know is (maybe this is a very stupid question) the info regarding what changes to be made and the requestor details should be separated in the separate database or in the sqlpersistence? any advice in the right direction would be muchly appreciated.
thanks

#1
Keyvan Nayyeri
10.15.2006 @ 9:10 AM