In the previous part of this open-ended series, we have discussed the issue of smuggling external transactions into a workflow.
Today, we will look into a strange scenario where allegedly asynchronous calls are not actually executed asynchronously.
The workflow services samples distributed in the MSDN and as part of the Visual Studio 2008 training kit almost always use synchronous communication. However, it is often the case that you want fire-and-forget behavior within a workflow service calling an external operation. In that case, you will merrily put IsOneWay=true in your [OperationContract] attribute, and move on.
However, the operation will only be executed asynchronously if the external service uses the BasicHttpBinding. With all other bindings I tried*, the operation is not actually invoked asynchronously. What happens is that only when the work on the external service’s end is completed, the SendActivity returns and the workflow can continue executing.
To demonstrate this, I used the following simple workflow that calls out to a WCF service:
The first code activity sets up the parameters to the service call. The SendActivity (called DuplexOperation here) calls the WCF service. The second code activity outputs a string to the console to indicate that the SendActivity has completed. The WCF service on the other end issues a Thread.Sleep, and has the following (one-way) interface:
public interface IMyService
[OperationContract(IsOneWay = true)]
void AsyncOperation(TimeSpan sleepSpan);
Unless the WCF service is using the BasicHttpBinding, the SendActivity doesn’t return and the code activity doesn’t get a chance to execute until the operation on the service side completes the Thread.Sleep call and returns. This is easily observable by running the workflow and checking your wrist watch for the time it takes for the second code activity to output something to the console.
The sample code used in this post can be downloaded as a Visual Studio 2008 solution (10KB).
* To be fair, I didn’t try the bindings that do not work with the context channel, such as the MSMQ binding. But hey, they do not work at all, synchronously or asynchronously 🙂