Nov 6, 2011

Attaching to WF4 runtime events from an IIS/WAS service

How do we attach our WorkflowServiceHostFactory to workflow level runtime events in WF4? Particularly for workflows hosted in IIS/WAS? If you're in the same boat as me, you've probably been spending more than a few minutes trying to figure out how to do this. I think in WF3 it was pretty straightforward, but in WF4 we have to approach this from a different angle.

Let's cut to the chase, here is what needs to be done to track the events we want:

Subclass from TrackingParticipant and override its Track method. Within this Track method we'll look only for TrackingRecords of type WorkflowInstanceRecord, since we only want to capture workflow level events.

NOTE: you can also attach to activity and custom level events but that's out of the scope of this blog post. I recommend this article for a more in depth description of Tracking: http://www.codeproject.com/KB/WF/WF4Extensions.aspx

If you want your application to "react" to particular workflow events, we only need to inspect the WorkflowInstanceRecord.State property. The following sample code is a generic template you can use with all the possible states I've encountered so far:

using System.Activities.Tracking;

public class WorkflowStateTrackingParticipant :TrackingParticipant
{
     protected override void Track(TrackingRecord record, TimeSpan timeout)
     {
          WorkflowInstanceRecord workflowInstanceRecord = record as WorkflowInstanceRecord;

          if(workflowInstanceRecord != null)
          {
               switch(workflowInstanceRecord.State)
               {
                    case "Started"break;
                    case "Aborted"break;
                    case "Canceled"break;
                    case "Completed"break;
                    case "Deleted"break;
                    case "Idle"break;
                    case "Persisted"break;
                    case "Resumed"break;
                    case "Successful"break;
                    case "Suspended"break;
                    case "Terminated"break;
                    case "UnhandledException"break;
                    case "Unloaded"break;
                    case "Unsuspended"break;
                    defaultbreak;
               }
          }
     }
}

To use this class, we need to add it to the WorkflowExtensions collection of our WorkflowServiceHostFactory; we do this by subclassing from it and overriding the CreateWorkflowServiceHost methods:

     public class CustomWorkflowServiceHostFactory WorkflowServiceHostFactory
     {
          protected override WorkflowServiceHost CreateWorkflowServiceHost(System.Activities.Activity activity, Uri[] baseAddresses)
          {
               WorkflowServiceHost host = base.CreateWorkflowServiceHost(activity, baseAddresses);
               host.WorkflowExtensions.Add(new WorkflowStateTrackingParticipant());

               return host;
          }

          protected override WorkflowServiceHost CreateWorkflowServiceHost(WorkflowService service, Uri[] baseAddresses)
          {
               WorkflowServiceHost host = base.CreateWorkflowServiceHost(service, baseAddresses);
               host.WorkflowExtensions.Add(new WorkflowStateTrackingParticipant());

               return host;
          }
     }

Finally, we need to indicate this CustomWorkflowServiceHostFactory, instead of the default one, in your Web.config or .svc files (depending on your particular implementation). In my case, I create my .svc definition files from the .xamlx workflows, dynamically, through Web.config via the serviceHostingEnvironment section:

<serviceHostingEnvironment multipleSiteBindingsEnabled="true">
     <serviceActivations>
          <add relativeAddress="MyWorkflowService.svc"
               service="Workflow.xamlx"
               factory="CustomWorkflowServiceHostFactory" />
     </serviceActivations>
</serviceHostingEnvironment>

Verify you can hit your .svc from a browser and you're done.

That's it, hopefully this post can help people save more than a few minutes of research.

No comments:

Post a Comment