Custom WorkflowPersistenceServices
manu
WWF Beta 2 comes along with a SqlWorkflowPersistenceService for persisting the state of longrunning workflows. As this service only works with Microsoft SQL Server or the Express Edition, you might think about creating your own persistence service which could target any other storage medium, like other relational databases, files, or - in this case - datasets.
For a short introduction to the WWF persistence services see [1], a comprehensive overview of a workflow’s lifecycle (and the persistence points) is described in [2].
As a first simple approach, i tried storing the workflows in a dataset with one table, which consits of the following columns:
Info : string
InstanceID : Guid
DateTime : DateTime
Activities : byte[]
StateID : Guid
Status : int
Type : int
Unlocked : boolean
To implement a custom persistence service for your workflows, you just have to inherit from WorkflowPersistenceService (in System.Workflow.Hosting) and override the following methods:
protected override Activity LoadWorkflowInstanceState(Guid instanceId);
protected override void UnlockWorkflowInstanceState(Activity state);
protected override void SaveCompletedContextActivity(Activity rootActivity);
protected override Activity LoadCompletedContextActivity(Guid activityId, Activity outerActivity);
protected override bool UnloadOnIdle(Activity activity);
where SaveWorkflowInstanceState, LoadWorkflowInstanceState, SaveCompletedContextActivity and LoadCompletedContextActivity are the important ones to fill in.
There are two main methods for serialization and deserialization:
{
if (rootActivity == null)
{
throw new ArgumentNullException(“rootActivity”);
}
PersistentWorkflow pwf = new PersistentWorkflow();
PersistentWorkflow.WorkflowInstanceRow newRow = pwf.WorkflowInstance.NewWorkflowInstanceRow();
WorkflowStatus status = (WorkflowStatus) rootActivity.GetValue(WorkflowInstance.WorkflowStatusProperty);
bool blocked = (bool) rootActivity.GetValue(WorkflowInstance.IsBlockedProperty);
string info = (string) rootActivity.GetValue(WorkflowInstance.SuspendOrTerminateInfoProperty);
Guid guid = (Guid)rootActivity.GetValue(Activity.ActivityContextGuidProperty);
newRow.InstanceID = (Guid) rootActivity.GetValue(WorkflowInstance.WorkflowInstanceIdProperty);
if ((status != WorkflowStatus.Completed) && (status != WorkflowStatus.Terminated))
{
newRow.Activities = WorkflowPersistenceService.GetDefaultSerializedForm(rootActivity);
}
else
{
newRow.Activities = new byte[0];
}
newRow.Status = (int) status;
newRow.Blocked = blocked;
newRow.Info = info;
newRow.StateID = guid;
newRow.Unlocked = true;
TimerEventSubscriptionCollection collection1 = (TimerEventSubscriptionCollection) rootActivity.GetValue(WorkflowInstance.TimerCollectionProperty);
TimerEventSubscription subscription1 = collection1.Peek();
newRow.DateTime = (subscription1 == null) ? DateTime.MaxValue : ((DateTime) subscription1.ExpiresAt);
pwf.WorkflowInstance.AddWorkflowInstanceRow(newRow);
string filename = FBaseDir + “\\“ + id.ToString() + “.xml”;
pwf.WriteXml(filename);
}
private object DeserializeActivity(Activity RootActivity, Guid id)
{
string filename = FBaseDir + “\\“ + id.ToString() + “.xml”;
PersistentWorkflow pwf = new PersistentWorkflow();
pwf.ReadXml(filename);
byte[] instanceState = ((PersistentWorkflow.WorkflowInstanceRow)pwf.WorkflowInstance.Rows[0]).Activities;
Activity result = RestoreFromDefaultSerializedForm(instanceState, null) ;
return result;
}
As you can see, deserializing the workflow instance is just calling one method (coming with the baseclass) on the activities entry of the dataset.
Now you might wonder why the other information is persisted when serializing: you will need when managing long running workflows whose lifetime exceeds that of the application’s instance, maybe i will cover that one in another post.
Invoking the (de)serialization is done in the four load and save methods you have to override:
{
SerializeActivity(rootActivity, (Guid)rootActivity.GetValue(Activity.ActivityContextGuidProperty));
}
protected override Activity LoadWorkflowInstanceState(Guid instanceId)
{
object obj = DeserializeActivity(null, instanceId);
return (Activity)obj;
}
protected override void SaveCompletedContextActivity(Activity rootActivity)
{
ActivityExecutionContext contextInfo = (ActivityExecutionContext)rootActivity.GetValue(Activity.ActivityContextGuidProperty);
SerializeActivity(rootActivity, contextInfo.ContextGuid);
}
protected override Activity LoadCompletedContextActivity(Guid activityId, Activity outerActivity)
{
object obj = DeserializeActivity(outerActivity, activityId);
return (Activity)obj;
}
So, the framework does the main part of the work, with GetDefaultSerializedForm and RestoreFromDefaultSerializedForm from the WorkflowPersistenceService baseclass.
For using this service in your application, you just have to add the persistence service to your workflow runtime:
Comments appreciated, but keep in mind that this one was just a first quick and dirty approach, so be gentle.
[1] Window Workflow Foundation Runtime Services: The Persistence Service
[2] Managing Workflow’s Lifecycle
tags:
workflow persistence, workflow state persistence, WorkflowPersistenceService, dataset, workflow state dataset, workflow persistence dataset, custom workflow persistence, wwf, windows workflow foundation, netfx3, .net 3.0Posted in windows workflow foundation |
|


October 17th, 2006 at 18:56
[…] Custom WorkflowPersistenceServices - Another great article by Manuel Foerster on the Persistence Service and writing your own. […]
November 8th, 2006 at 07:33
Thanks for this wonderfull example. But my problem is that i m not able to see SaveCompletedContextActivity and LoadCompletedContextActivity methods getting executed. Please let me know if you know anything about it. I have already tried adding compensatableTransaction scope activity in to my workflow. But still when the scope is completed this two methods are not being invoked.
November 8th, 2006 at 17:23
kaushal,
thanks for the feedback!
unfortunately, i haven’t been working with wwf for the last few months.
i think there have been major changes in the framework since i posted the example above, originally it was created using beta 1.2.
however, in the meantime microsoft published a (rudimentary) article on custom persistence services (see http://msdn.microsoft.com/library/default.asp?url=/library/en-us/WF_GettingStarted/html/6b1ed562-952a-4c68-b294-a093503cf454.asp), maybe that will help. if not, please drop me a line.
perhaps i’ll update my example later this month.
December 15th, 2006 at 14:56
hi i am new to workflow foundations. trying to build cusom persistence service.
in the example by u i find a class “PersistentWorkflow” has been used which has some static members as wel.
i am not getting the class. i am using NovCTP. can u let me know where do i get that “PersistentWorkflow” class?
December 15th, 2006 at 20:48
mausam,
sorry for that, i missed to point out that the dataset i described above would be called ‘PersistentWorkflow’.
if you need some more hints, let me know.
February 27th, 2007 at 12:51
Nice article, I have question, as you mentioned GetDefaultSerializedForm and RestoreFromDefaultSerializedForm methods do the serialisation. However, they are limited to only Serializing via a binary formatter. I have a situation where I want to query the work flow state in the data store using a XML type (sql server 2005).
I’m surprised there is no configuration setting that sets the type of serialisation to use, just like a majority of the .net providers such as the profiler provider (SerializeAs=”Xml”).
I’ve been surfing the net looking for a scenario where a custom SQL work flow persistence state is used that reads/writes to the underling database, but with no luck. I would much appreciate if you could direct me or post an implementation.
If your interested I’m planning on doing the below to meet my need
1) create custom db table to contain the xml string(Serialized object).
2) 2) custom sql work flow persistence service
If you have any suggestion please do respond.
If I’m successful and you’re interested in the solution, I’ll let you know.
Cheers
Sonny M
May 23rd, 2007 at 13:08
Hai,
I am a Beginner to WWF.
ur information is not clear.
can u give a detailed solution.
May 23rd, 2007 at 13:22
hi,
the article should be a hint on how to implement custom persistence services rather than a complete solution.
if you have specific questions on that topic, let me know.
April 18th, 2008 at 00:56
I have a problem with a custom implementation of the Windows Workflow Persistence Service that writes to file. If the host application quits while there are workflow instances on idle states due to delay activity, when loads again I need to manually tell the runtime “WF_Runtime.GetWorkflow(InstanceId)” even when the workflow instance time of delay aren’t finished yet. Doing so makes everything works, but if not the instances never load again even when the delay activity’s time finish. Although if I use an ExternalCall activity the “GetWorkflow”’s call is not necessary. At the same time if I never close the host application both types of activities works fine without the call.
What do you thing I’m doing wrong?
The implementation of the file persistence service I’m testing and changing is the one given on the Windows Vista and Framework 3 SDK.