One of the new power features in .NET 3.5, sorely missing when .NET 3.0 was released
is the integration between Windows Communication Foundation and Windows Workflow Foundation,
in the way of the new Workflow Services model. Besides providing an easy way to invoke
WCF services from WF without resorting to using BasicHttpBinding on the services or
invoking a [wrapper] client class form a code activity in your workflow, it also
provides a way to expose a workflow as a WCF service to the outside.

The second functionality is the one that, naturally, interests me the most. After
looking at it for a while, I’ve made a few observations that I’d like to share.

Implementing Services

A very interesting thing is how you implement services as workflows. If you know
how to create WCF services the right way, then you [mostly] know how to implement
services as workflows. The reason for this is that most of the tasks you’d normally
when creating a WCF service still apply when creating a workflow service. That is,
you will still declare your service contract (interface) as well as your message/data
contracts. The only key differences really is how you implement that contract.

When using workflow services, you implement a contract by using the new Receive activity
introduced in .NET 3.5, and select which operation of the contract you’d
be implementing. You still need to do the activity binding dance for parameters as
well as (possibly) create response messages for the incoming requests. Basically,
the Receive activity works as a kind of sequential activity where the child activities
represent the tasks to execute after the request message is received until the response
message is sent (if this is a request/response service).

You’d also need to do a lot of the usual work in setting up your service host, only
instead of using the regular ServiceHost class you’ll be using the new WorkflowServiceHost
class. Guy Burstein has a good entry on getting started on exposing workflows as services here you
might want to check out.

Starting Workflows

One very interesting option in the Receive Activity is the CanCreateInstance
property. You can use this property to have the WorkflowServiceHost automatically
create and start a new instance of your Workflow when a new message arrives at the
service endpoint for the implemented operation, without having to write manual
code to receive the message and create the new workflow instance.

People familiar with BizTalk 2004/6 will recognize this option as being similar to
the Activate property of the Receive Shape in BizTalk orchestrations. It also serves,
in a way, a similar purpose as the CanCreateInstance property of the new [DurableOperationBehavior]
attribute introduced for WCF in .NET 3.5 as part of WCF Durable Services infrastructure.
Jesus Rodriguez has an excellent
article
introducing this new feature.

Something I found a bit curious about the CanCreateInstance property of the Receive
Activity is that there’s currently no validation around where you set it. As far as
I can see, it doesn’t make much sense to use it unless the receive activity is the
entry point of your workflow, but currently nothing prevents you from setting it if
is not. That said, I guess it might be difficult to validate this correctly given
the WF execution model.

Continuing Workflows

The Receive Activity doesn’t limit you to starting new workflow instances; it also
allows your workflow instance to wait until a message is received (through a service
invocation from a consumer) to continue execution. This is a key scenario for long-running
processes because the workflow will have all the necessary context within the business
process to interpret the coming message correctly and act on it.

How does the host know which workflow instance should process a given message, then?
The answer lies in the binding context. WF requires the consumer of the service to
send a token alongside the request message that tells it what workflow instance the
message is for. This token is, essentially, the WorkflowInstanceId, and can be sent
either as an HTTP Header or as a SOAP Header.

Using the WorkflowInstanceId as the instance selection token is a logical choice for
WF, as the Workflow Runtime, as well as the persistence service, already have clear
knowledge of what the ID means and how to use it, so getting the right workflow instance
to pass the message to is basically just a call to the GetWorkflow() method of the
WorkflowRuntime object.

For this entire process to work, both the server and the client must use the special
WSHttpBindingContext class, which helps makes this process a bit more transparent
(particularly for the workflow service). On the client side, naturally, the service
consumer must be, somehow, aware of the correct workflow instance ID to use as the
token. Sometimes, this will be handled in a very transparent way by the infrastructure,
but sometimes you’ll need to make it more explicit. And by the way, the WSHttpBindingContext
class is also used for WCF Durable Services.

Serge Luca has an extensive
sample
of using WF Services in several scenarios that is pretty interesting to
check out.

The Limitation of IDs

This entire mechanism works fine for WF Services. However, after I saw this the first
time, there was something that I didn’t quite like about it, and it kept nagging in
my head. A few days ago I finally realized what it was: As a first option for having
this feature, it is pretty useful, but it is, in a way, a significant step back.

The problem with using the WorkflowInstanceId as the linking element between the service
consumer and the workflow implementing the service is that it has, essentially, no
business meaning. While it is a very useful technical discriminating ID, most business
scenarios where this kind of long running services and processes are involved already
have natural correlating identifiers
in the problem domain, like a purchase order
id, a loan id, or a combination of customer id and tracking id.

There are several implications of this simple fact:

  • Consumers of the service become aware of the fact that there’s a workflow on
    the other end of the service. This is actually merely an implementation detail of
    the service which should be transparent to the consumer as much as possible.
  • Consumers might now be forced to track yet another id, and one that has no business
    meaning at all. So instead of just having, say, the order id around they also need
    to find where to store the workflow id.
  • The WorkflowInstanceId becomes an added part of the service messaging, but it is one
    you might not always be aware of because it doesn’t make part of the core messaging
    description.

Of all of this, I think the second one is what bothers me the most. If you’re ever
used the wonderful Correlation Set mechanism introduced in BizTalk 2004, then you’ll
definitely see where I’m coming from, as this is a bit more restrictive and less expressive
than what we can do right now with BizTalk. In fact, this is more akin to the limited
correlation mechanism we had back in BizTalk
2002
(though that required jumping through a lot more hoops).

Conclusion

All in all, this seems like a very welcome addition to both WCF and WF, even though
there are limitations in this first release. I’ll be watching closely how both develop
as they take a bit more of the core messaging and orchestration strengths that Biztalk
has enjoyed for the past few years.