If you are building Windows Workflow Foundation (WF) applications today (and you aren’t
building Sharepoint Workflows) you should be using WorkflowServiceHost for your hosting
environment. Period, end of discussion (oh – well we can have more decision
about it – but its a fait accomplis at this point).
Especially if you are using WF to implement a service, using .NET 3.5 is a total no
brainer.
I was doing so the other day using a StateMachineWorkflow hosted in IIS and I was
getting an exception. The exception (which I found after I attached the debugger
to IIS) was the dreaded (and pretty common) exception “QueueNotFound for queue X”.
Now – if I was writing the hosting layer myself I would handle the WorkflowRuntime.WorkflowIdled
event and introspect on the WorkflowInstance using WorkflowInstance.GetWorkflowQueueData
method to see what queues where available at different points during the workflow
to help figure out what the problem was. The problem was that I expected the
Queue to be there at that paritcular point of execution, and I wanted to verify that
fact using the WorkflowInstance.GetWorkflowQueueData method like this:
void WorkflowRuntime_WorkflowIdled(object sender,
System.Workflow.Runtime.WorkflowEventArgs e)
{
ReadOnlyCollection<WorkflowQueueInfo> queues;
queues = e.WorkflowInstance.GetWorkflowQueueData();
foreach (WorkflowQueueInfo
qi in queues)
{
Debug.WriteLine(“QueueName
: “ + qi.QueueName);
foreach (string actName in qi.SubscribedActivityNames)
{
Debug.WriteLine(“Activity
subscribed: “ + actName);
}
}
}
This is code I end up writing in just about every Workflow application I build because
it is just super useful to know what Queues a workflow is listening for at a particular
time.
So here was my problem – since I was using WorkflowServiceHost implicitly through
an svc file:
<%@
ServiceHost
Service=“WorkflowArtifacts.CalcWorkflow”
Factory=“System.ServiceModel.Activation.WorkflowServiceHostFactory” %>
using WorkflowServiceHostFactory, I had no place to get the WorkflowRuntime from the
WorkflowServiceHost. If I was creating WorkflowServiceHost myself in code –
I could use the following code to get the WorkflowRuntime:
//sh
is the WorkflowServiceHost
WorkflowRuntimeBehavior wrb = sh.Description.Behaviors.Find<WorkflowRuntimeBehavior>();
wrb.WorkflowRuntime.WorkflowIdled += new EventHandler<System.Workflow.Runtime.WorkflowEventArgs>(WorkflowRuntime_WorkflowIdled);
Using the WorkflowRuntimeBehavior – I can get the WorkflowRuntime and then subscribe
to the WorkflowIdled event – and thus be able to the get data I wanted for debugging
my “QueueNotFound for queue X” exception. But alas – using the svc file I don’t ever
get access to the WorkflowServiceHost.
There is however a solution (at least one) – ServiceHostFactoryBase. One
of the cool extensibility mechanisms with WCF you can take advantage of when hosting
inside of IIS/WAS is creating your own ServiceHostFactory. By default with code-based
services – WCF uses a ServiceHostFactory (which is a derived class from ServiceHostFactoryBase)
to create the WCF ServiceHost (which derives from ServiceHostBase) for hosting a code-based
service. For Workflow Services – they use a WorkflowServiceHostFactory (as you
can see from my svc file) to create the WorkflowServiceHost.
Luckily (and I’m sure intention on the framework team’s part) WorkflowServiceHostFactory
isn’t sealed. What this means is that I can create my own WorkflowServiceHostFactory
class – change the Factory attribute in my svc file to point to my factory – and then
insert the code I wanted to work with the WorkflowRuntime object.
Here’s my WorkflowServiceHostFactory class:
public class MyWorkflowServiceHostFactory
: WorkflowServiceHostFactory
{
public override System.ServiceModel.ServiceHostBase
CreateServiceHost(string constructorString,
Uri[] baseAddresses)
{
ServiceHostBase sh = base.CreateServiceHost(constructorString,
baseAddresses);
//sh
is the WorkflowServiceHost
WorkflowRuntimeBehavior wrb = sh.Description.Behaviors.Find<WorkflowRuntimeBehavior>();
wrb.WorkflowRuntime.WorkflowIdled += new EventHandler<System.Workflow.Runtime.WorkflowEventArgs>(WorkflowRuntime_WorkflowIdled);
return sh;
}
void WorkflowRuntime_WorkflowIdled(object sender,
System.Workflow.Runtime.WorkflowEventArgs e)
{
ReadOnlyCollection<WorkflowQueueInfo> queues;
queues = e.WorkflowInstance.GetWorkflowQueueData();
foreach (WorkflowQueueInfo
qi in queues)
{
Debug.WriteLine(“QueueName
: “ + qi.QueueName);
foreach (string actName in qi.SubscribedActivityNames)
{
Debug.WriteLine(“Activity
subscribed: “ + actName);
}
}
}
}
And my svc file:
<%@
ServiceHost
Service=“WorkflowArtifacts.AccumWorkflow”
Factory=“MyWorkflowServiceHostFactory” %>
And magically my service still works, and I am able to handle the WorkflowRuntime.WorkflowIdled
event – or any other WorkflowRuntime event I want.
Check out my BizTalk
R2 Training.