When we start doing this two way style of messaging, we now open up to start modeling some interesting business problems. In the previous post, you'll note that I did not include the code, because I mentioned we needed to be more clever in scenarios where we listen in parallel.
First, a brief diversion into how the Receive activity works. Everybody remembers the workflow queues, the technology that underlies all communication between a host and a workflow instance. The Receive activity works by creating a queue that the WorkflowServiceHost (specifically the WorkflowOperationInvoker) will use to send the message received off the wire into the workflow. Now, the Receive activity normally just creates a queue that is named the same as the operation the Receive activity is bound to. However, if we have two Receive activities listening for the same operation at the same time, no longer is a single queue useful to route responses back as we want to route to the correct Receive activity instance.
There is property on the Receive activity called ContextToken. Normally this is null in the simple case. However, when we want our Receive activity to operate in parallel, we need to indicate that it needs to be smarter when it creates a queue.
By setting this property (you can just type in a name, and then select the common owner all of the parallel receive's share. This will cause the Receive activity to create a queue named [OperationName] +[ConversationId], the conversation ID takes the form of a GUID, and is the second element inside a context token.
The sample that I show for this talk is simply the conversations sample inside the SDK. This is the sample to check out to understand all sorts of interesting ways to use the context tokens to model your processes.
Now, there are two conversation patterns here. One is the one shown above, which I refer to as an n-party conversation where n is fixed at design time. We can accomplish this with the parallel activity. The other is where n is arbitrary (imagine you send out to business partners stored in the database). The way to do this is to use the Replicator activity. The Replicator is a little known gem shipped in 3.0 that essentially gives you "ForEach" semantics. But, by flipping the ExecutionType switch to parallel, I now get the behavior of a parallel, but operating with an arbitrary n branches.
So, in order to enable conversations, we need to tell our receive activity to be a little smarter about how it generates its queue name, and then we simply follow the duplex pattern we discussed in the last two posts. Once we do that, we're in good shape to start modeling some more interesting communication patterns between multiple parties.
Where can we go from here?
We can just make the patterns more interesting. One interesting one would be the combination of the long running work with cancellation and a Voting activity in order to coordinate the responses and allow for progress to be made when some of the branches complete (if I have 3 yes votes, I can proceed). The power of building composite activities is that it gives me a uniform programming model (and a single threaded one to boot) in order to handle the coordination of different units of work. Get out there and write some workflows 🙂