In WF3, when a workflow is delayed and persisted in persistence store, after the timer expired, workflow will resume from database automatically.Now in WF4, we have to resume a persisted workflow manually, So can we create a long running WF4 that can monitor a delayed workflow and resume a workflow automatically after the delay timer expired. Here is a class that can achieve this:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using System.Threading;
using System.Activities.DurableInstancing;
using System.Runtime.DurableInstancing;
using System.Activities;
using System.Configuration;
public class LongRunningWFHost{
Activity workflow=null;
ManualResetEvent waitHandler=new ManualResetEvent(false);
static XName wfHostTypeName;
bool completed = false;
private static readonly XName WorkflowHostTypePropertyName =
XNamespace.Get("urn:schemas-microsoft-com:System.Activities/4.0/properties").GetName("WorkflowHostType");
SqlWorkflowInstanceStore instanceStore = null;
InstanceHandle instanceHandle = null;
public LongRunningWFHost(Activity workflow){
this.workflow=workflow;
}
public void Run(){
wfHostTypeName = XName.Get("Version" + Guid.NewGuid().ToString(),
typeof(WorkflowWithDelay).FullName);
this.instanceStore = SetupSqlpersistenceStore();
this.instanceHandle =
CreateInstanceStoreOwnerHandle(instanceStore, wfHostTypeName);
WorkflowApplication wfApp = CreateWorkflowApp();
wfApp.Run();
while (true) {
this.waitHandler.WaitOne();
if (completed) {
break;
}
WaitForRunnableInstance(this.instanceHandle);
wfApp =CreateWorkflowApp();
try {
wfApp.LoadRunnableInstance();
waitHandler.Reset();
wfApp.Run();
} catch (InstanceNotReadyException) {
Console.WriteLine("Handled expected InstanceNotReadyException, retrying...");
}
}
Console.WriteLine("workflow completed.");
}
public void WaitForRunnableInstance(InstanceHandle handle) {
var events=instanceStore.WaitForEvents(handle, TimeSpan.MaxValue);
bool foundRunnable = false;
foreach (var persistenceEvent in events) {
if (persistenceEvent.Equals(HasRunnableWorkflowEvent.Value)) {
foundRunnable = true;
break;
}
}
if (!foundRunnable) {
Console.WriteLine("no runnable instance");
}
}
public WorkflowApplication CreateWorkflowApp() {
WorkflowApplication wfApp = new WorkflowApplication(workflow);
wfApp.InstanceStore = this.instanceStore;
Dictionary<XName, object> wfScope = new Dictionary<XName, object>{
{ WorkflowHostTypePropertyName, wfHostTypeName }
};
wfApp.AddInitialInstanceValues(wfScope);
wfApp.Unloaded = (e) => {
Console.WriteLine("Unloaded");
this.waitHandler.Set();
};
wfApp.Completed = (e)=>{
this.completed=true;
};
wfApp.PersistableIdle = (e) => {
return PersistableIdleAction.Unload;
};
wfApp.Aborted = delegate(WorkflowApplicationAbortedEventArgs abortArgs) {
Console.WriteLine("Workflow aborted (expected in this sample)");
};
return wfApp;
}
private SqlWorkflowInstanceStore SetupSqlpersistenceStore() {
string connectionString =
ConfigurationManager.AppSettings["SqlWF4PersistenceConnectionString"].ToString();
SqlWorkflowInstanceStore sqlWFInstanceStore = new SqlWorkflowInstanceStore(connectionString);
return sqlWFInstanceStore;
}
// Configure a Default Owner for the instance store so instances can be re-loaded from WorkflowApplication
private static InstanceHandle CreateInstanceStoreOwnerHandle(InstanceStore store,
XName wfHostTypeName) {
InstanceHandle ownerHandle = store.CreateInstanceHandle();
CreateWorkflowOwnerCommand ownerCommand = new CreateWorkflowOwnerCommand() {
InstanceOwnerMetadata = {
{ WorkflowHostTypePropertyName, new InstanceValue(wfHostTypeName) }
}
};
store.DefaultInstanceOwner = store.Execute(ownerHandle, ownerCommand,
TimeSpan.FromSeconds(30)).InstanceOwner;
return ownerHandle;
}
}
Source Code: CSWF4LongRunningHost.zip (60.11 kb)
c8457e71-09ca-45f5-82a7-cac87dbae1c1|0|.0
WF4
WF4