[WF4]Long Running WF4 Host

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)

blog comments powered by Disqus