DCSIMG
Dynamic Reply Endpoints in a Duplex Workflow Service - All Your Base Are Belong To Us

All Your Base Are Belong To Us

Mostly .NET internals and other kinds of gory details

Dynamic Reply Endpoints in a Duplex Workflow Service

The Workflow Services paradigm in .NET 3.5 gives us the ability to expose a workflow as a WCF service, and to communicate with a WCF service from within a workflow.  These useful scenarios are enabled by the ReceiveActivity and the SendActivity respectively.  (If all of this sounds new, check out Guy Burstein's primer on Workflow Services.)

The built-in SendActivity is great until you need to provide custom endpoint information.  Basically, there are three options, in rising degree of complexity:

Option 1 (Fully static)

Statically configure SendActivity.ServiceOperationInfo (specifying which method on which contract type you want to invoke), SendActivity.ChannelToken and SendActivity.ChannelToken.EndpointName (specifying the endpoint name from your application's configuration file). - EASY

image

Option 2 (Only endpoint address is dynamic)

Do all the above but dynamically set the SendActivity.CustomAddress property to redirect the invocation to a different endpoint address.  The binding, contract and endpoint behavior all remain the same because they are still specified in the application's configuration file. - RELATIVELY EASY

image

Option 3 (Endpoint is fully dynamic)

Do not provide an application configuration file.  Configure the workflow runtime so that it (and you) can determine, in run-time, what the corresponding endpoint for a SendActivity should be. - DIFFICULT

So what do you need to do to enable this highly flexible third scenario?  The WorkflowRuntime hosted in your WorkflowServiceHost uses a service called the ChannelManagerService to pool and locate channels whenever a SendActivity needs to perform an invocation outside the workflow.  Therefore, to customize the endpoints, you need to provide your own instance of the ChannelManagerService class before starting the workflow host, and that ChannelManagerService instance will be pre-configured with your endpoints (also called "code endpoints").  By the way, these code endpoints are preferred over endpoints specified in the configuration file, just as you might expect.

Here's how you do it.  In my sample, the workflow contains a ReceiveActivity which waits for an invocation of IMyWorkflow.Do.  After that, there's a SendActivity which invokes the IMyWorkflowCallback.Done method.  So this is the workflow and the interfaces:

image image

Next, we write a callback object which will receive the IMyWorkflowCallback invocation.  Here's the callback object:

image

Now we bring that callback object up as a service:

image

Next, we initialize a ChannelManagerService instance to resolve the endpoint to our callback in runtime (note that the endpoint name here has to match the endpoint name from the SendActivity's channel token):

image

Finally, we bring up a WorkflowServiceHost, find the WorkflowRuntime, add the ChannelManagerService as a workflow runtime service, add the workflow endpoint (for the ReceiveActivity) and start up the host.

image

All that's left is test the implementation by creating a workflow by sending a message to the service, and seeing that we receive a reply to the callback object created earlier:

image

After coding this up, I've had another idea which would give me even more flexibility.  Why wouldn't I write a ChannelManagerService class of my own (or derive from the existing one), and provide the actual endpoint dynamically?  I could have an event to be fired whenever an endpoint had to be resolved, and there I have a fully extensible implementation.  Sadly, this is plain impossible.  First of all, the ChannelManagerService class is sealed.  Second, when the SendActivity is looking for a channel, it's not just asking the ActivityExecutionContext to provide a ChannelManagerService instance.  It goes and calls ChannelManagerService.Take, which is an internal method, directly!  (How that is supposed to be compliant with basic guidelines for using workflow runtime services, I do not know.)  So this means I have to derive from SendActivity and completely replace the base class' implementation of Execute, by means of duplication - and I'm certainly not going to do that.

The code demonstrated in this post can also be downloaded from here, as a Visual Studio 2008 solution.

(These steps are also performed, without too detailed a documentation, in the WorkflowServiceUtility MSDN sample.  To check it out, download the samples - a modest 22MB self-extracting archive - extract them to some directory, and navigate to the WCF\Scenario\WorkflowServices\WorkflowServiceUtility\CS sub-folder.  The LocalWorkflowServiceHost.cs file contains a sample implementation which accomplishes the above requirement by deriving from WorkflowServiceHost and providing a ChannelManagerService instance.)

Comments

Sam Gentile said:

Last N&N was on the 9th and I don't know if I will able to maintain the series. I am extremely

# January 21, 2008 4:39 PM

Sam Gentile's Blog said:

Last N&N was on the 9th and I don't know if I will able to maintain the series. I am extremely busy, and traveling on multiple projects. I also am not sure that my interests are what the current .NET blogging climate and community want to hear about

# December 2, 2008 6:46 PM
Leave a Comment

(required) 

(required) 

(optional)

(required) 


Enter the numbers above: