Using Windows Communication Foundation with Windows Workflow Foundation – Part 1

Both Windows Communications Foundation (WCF) and Windows Workflow Foundation (WF) were released at the same time as part of the .NET 3.0 release. But even though they were released together, they didn’t really work together. The main culprit here was Windows Workflow Foundation, which only contains activities for connecting to and publishing workflows as web services. This lack of WCF support was fixed in the .NET 3.5 release when the WF team added two new activities. The first of these is the SendActivity, which enables you to call WCF as well as standard web services. The second is the ReceiveActivity, which enables a developer to publish a workflow as a WCF service. In this first article I will take a look at how you can use the SendActivity in your workflows. In a next article I will be taking a closer look at the ReceiveActivity and the capabilities it has to offer.

Sending requests to remote services

When we want to send requests to a remote service, we can use the SendActivity. Using the SendActivity is quite straightforward, especially with a WCF service as the target. Because WCF is backwards-compatible with the more traditional ASMX style web services, you can also use the SendActivity to work with these services; however, as we will see, this requires a bit more work, and using the InvokeWebServiceActivity can be easier.

Calling Windows Communication Foundation services

The function of the SendActivity is to call a WCF service from within a workflow. The best way to explain how to use it is by creating a simple example. In this example I am using Visual Studio 2008, which comes with the .NET 3.5 Framework. The first step is to create a new Visual Basic Sequential Workflow Console Application (see Figure 1). Let’s call it SimpleSend.

1_CreateSimpleSend.png

Figure 1. The New Project dialog box to create the workflow application

When the project is created, it will automatically open the default workflow, with the name Workflow1, in the designer. If you now open the Toolbox and scroll to the bottom (see Figure 2), you will see a section named Windows Workflow v3.5, which contains the two new activities.

2_WF35Toolbox.png

Figure 2. The toolbox showing the new Windows Workflow Foundation 3.5 activities

Before we go on, we first need to add a WCF service to call. Let’s add to the solution a second project of type WCF Service Library, found under the WCF tab, and call it SimpleService. This new project contains a very basic WCF service with the name IService1 and a function GetData that takes an integer as parameter and returns a string. This simple interface will do just fine for the first sample, so just leave it as it is and build the solution.

To call this WCF service from our workflow, we need to add a SendActivity to the workflow. The activity will show up displaying a red exclamation mark at the right top indicating that it isn’t configured properly yet (see Figure 3). The first thing to notice here is a difference compared with the InvokeWebService activity that was released as part of the .NET 3.0 Framework. When an InvokeWebService activity is dropped on a workflow, it displays the “Add Web Reference” dialog to add the required web reference.

3_SimpleSendWithSendActivity.png

Figure 3. Adding a SendActivity to the new workflow

In the case of the SendActivity the “Add Service Reference” dialog box used to add a WCF service doesn’t automatically appear. As mentioned previously, the SendActivity isn’t configured properly, and the first thing it needs is a service operation to call. This is set using the ServiceOperationInfo property, but before we can do this, we need to add the service reference ourselves. To do so, right-click the SimpleSend project in the Solution Designer and select “Add Service Reference…” to add this reference. In the dialog box (see Figure 4), click Discover to search for the SimpleService in our solution. When we expand the service in the services tree, Visual Studio will automatically start the new WCF service host, named WcfSvcHost. This service host is similar to the simple web server included with Visual Studio to make ASP.NET development easier. It means you can create a WCF assembly without having to worry about hosting it while developing. Next, change the namespace to SimpleService and click OK to add the reference.

4_AddSimpleServiceReference.png

Figure 4. Adding a reference to the WCF SimpleService

Having added the service reference, we can now configure the SendActivity itself. Click the button behind the ServiceOperationInfo in the property sheet to specify the service operation to execute. This will show an empty “Choose Operation” dialog box. To select an operation, we first need to import the service interface using the Import… button. Clicking this Import… button will display the type browser (see Figure 5). Select the IService1 interface and click OK. Once we have done this the “Choose Operation” dialog box will show the two operations available in the IService1 interface (see Figure 6). Select the GetData operation and click OK.

5_TypeBrowser.png

Figure 5. The type browser used to select the WCF interface

6_ChooseOperation.png

Figure 6. The Choose Operation dialog box

At this point the SendActivity knows what it is going to call and will display the service operation parameter and return value in the property sheet, as you can see in Figure 7. But the red exclamation mark indicates we aren’t done with the configuration yet. While we have specified what to call, we still need to specify where to call this service. Specifying where to call the service is done using the ChannelToken property. Once we specify a name for the service token (I am using Service1Token in this example), a plus sign appears before the property name and we can expand it. Doing so will show the EndpointName, which is what we need to add. This EndpointName must point to an endpoint defined in the application configuration file. This endpoint was added when we set the service reference to our WCF service. In this case our endpoint was added with the name WSHttpBinding_IService1, so that is what we need to specify. Once this is done, we are almost ready to run our workflow; the only thing left is to add the parameter and do something with the return value. Both the parameter and return value show up in the property sheet, but that is actually a designer trick, as there are no properties with these names.

7_ServiceParameters.png

Figure 7. The WCF service parameter and return value.

Sometimes you might want to be more dynamic with the location of the service called than storing it in the application configuration file. In that case we can use the CustomAddress property to set the address of the service dynamically at runtime.

In the case of the parameter, the value property in the property sheet, we can either add a fixed value or specify a property whose value should be passed. Passing a fixed value is easy—just type it into the property sheet—but using a workflow property is more common. To do this we use something called property binding. Property binding is a very powerful and simple-to-use feature found both in Windows Workflow Foundation and in Windows Presentation Foundation. To bind the value to send to a workflow property, click the “…” button behind the value in the property sheet. This will display the “Bind ‘value’ to an activity’s property” dialog box (see Figure 8) where you can either select an existing or add a new property to bind to. As we don’t have a property yet, we need to add a new one on the “Bind to new member” tab. For this example, enter a name of TheValue, select “Create Property,” and click OK to create the new property. Another way to add property bindings quickly for all bindable properties is to select the option “Promote Bindable Properties” from either the context menu or the commands at the bottom of the property sheet. This action will generate workflow dependency properties for all the activities’ bindable properties, including the BeforeSend and AfterResponse handlers, and bind to these new properties.

8_CreatePropertyBinding.png

Figure 8. Create a new property binding

We also need to create a property binding for the service operation return value named “(ReturnValue)” in the property sheet. Creating this is done in exactly the same way as the value parameter; use TheResult as the property name.

The SendActivity exposes two events we can use to set the parameter we pass in and display the result returned. The first is the BeforeSend event, which is fired, as the name suggests, before the WCF service operation is called. The second event is named AfterResponse and is called after the result is returned from the WCF operation. Listing 1 shows the two event handlers used. Note that I use a Console.ReadLine to enable us to read the output when the workflow is run. Please keep in mind that adding a blocking statement like this inside of a workflow event is normally not a good practice and should be avoided in a real workflow.

Private Sub BeforeGetData( _
    ByVal sender As System.Object, _
    ByVal e As System.Workflow.Activities.SendActivityEventArgs)
    Me.TheValue = DateTime.Now.Second
End Sub
Private Sub AfterGetData( _
    ByVal sender As System.Object, _
    ByVal e As System.Workflow.Activities.SendActivityEventArgs)
    Console.WriteLine("The result was '{0}'", Me.TheResult)
    Console.ReadLine()
End Sub

Listing 1. The SendActivity event handlers

Now we are ready to run the workflow, so go ahead and press F5 to start it. Just like when we added the service reference, the WCF Service Host automatically starts to host our web service. Once the workflow has started, it will call the WCF service and the output will look something like Figure 9. Note that the exact output might be different, as the value passed in depends on the current time.

9_RunningSimpleSend.png

Figure 9. Running our SimpleSend workflow

Using the SendActivity with a Web Service

Because WCF is backwards-compatible with ASMX-style web services, it is also possible to use the SendActivity to call a “traditional” web service. Let’s create a new project, called AsmxSend, to demonstrate how to do this. The first steps are the same, so create a new Sequential Workflow Console Application named AsmxSend. This time, add a second project to the solution, but use an ASP.NET Web Service Application, which is found under the Web tab. Let’s name the project AsmxService. This adds a new web service with its default HelloWorld web method. To let us compare this with the WCF service we just added, we are going to add the same GetData() function we used in that sample; this function can be found in Listing 2.

<WebMethod()> _
Public Function GetData(ByVal value As Integer) As String
    Return String.Format("You entered: {0}", value)
End Function

Listing 2. The GetData function used in the web service

As before, we need to add a service reference to the web service (see Figure 10). Make sure to add a service reference and not a Web Reference as found at the bottom under the advanced service reference settings. The reason we do not want to add a web reference is that this generates a different type of proxy, and the SendActivity needs a WCF-style proxy object to work with. For this example, use AsmxService as the namespace when adding the service reference.

10_AddAsmxServiceReference.png

Figure 10. Adding a service reference to an ASMX web service

Once we have added the service reference to the AsmxService we can add a SendActivity to the workflow in our AsmxSend project. Configure the SendActivity ServiceOperationInfo as before, selecting the Service1Soap as the type and GetDate as the operation to use. We also need to specify the ChannelToken and the EndpointName. This time the Endpoint added to the app.config is named “Service1Soap”.

Just like before, we need to add property bindings for the operation parameter and return value; use the same names as before, TheValue and TheResult. Although we don’t see any difference in the dialog box when adding the property bindings, something very different happens. The operation parameter is of type integer, and before, when we added a service reference to the WCF service, a property of type integer was added. But in this case, when using an ASMX web service, a property of type GetDataRequest is also created. This object of type GetDataRequest contains a single field, named Body, of type GetDataRequestBody, and this body in turn contains the actual integer value passed. The same is true with the return value, which is of type string. In the WCF service reference the type of TheResult was string, but with the ASMX service reference this becomes a property of type GetDataResponse which in turn contains a Body field of type GetDataResponseBody. This GetDataResponseBody finally contains the actual string value retuned in the GetDataResult field. So, while the code might be slightly more verbose, calling ASMX-style web services is just as easy as calling WCF services (see Listing 3).

Private Sub BeforeGetData( _
    ByVal sender As System.Object, _
    ByVal e As System.Workflow.Activities.SendActivityEventArgs)
    Me.TheValue = New AsmxService.GetDataRequest
    Me.TheValue.Body = New AsmxService.GetDataRequestBody
    Me.TheValue.Body.value = DateTime.Now.Second
End Sub
Private Sub AfterGetData( _
    ByVal sender As System.Object, _
    ByVal e As System.Workflow.Activities.SendActivityEventArgs)
    Console.WriteLine("The result was '{0}'", _
                      Me.TheResult.Body.GetDataResult)
    Console.ReadLine()
End Sub

Listing 3: The code used when calling an ASMX style web service.

One unexpected behavior is that we now see an error appearing in the workflow; see Figure 11 for an example. This error claims that the type of the property binding on TheResult is not the correct type. In fact, as the message claims, both are of type AsmxSend.AsmxService.GetDataResponse and the property binding is correct. So ignoring this error and running the workflow will result in the expected behavior.

11_PropertyBindError.png

Figure 11. The incorrect error message

Conclusion

As we have seen, using the Workflow Foundation SendActivity to talk to either a WCF or an ASMX service is quite straightforward. Working with an ASMX web service is a bit more work, and as there is little or no benefit of the SendActivity over the CallWebServiceActivity, using the latter might be more appropriate. This, however, is just half the story, as .NET 3.5 also comes with a ReceiveActivity and its job is to expose a workflow as a WCF service, similar to what the WebServiceInputActivity/WebServiceOutputActivity pair allowed us to do as an ASMX style web service. In the next article I will be taking a closer look at this ReceiveActivity.

About Maurice de Beijer

Maurice de Beijer is an independent software consultant specializing in Workflow Foundation and .NET in general. He has been awarded the yearly Microsoft Most Valuable Professional award since 2005. Besides developing software, Maurice also runs the Visual Basic section of the Software Development Network, the largest Dutch .NET user group. Maurice can be reached through his web sites, either www.WindowsWorkflowFoundation.eu or www.TheProblemSolver.nl, or by emailing him at Maurice@TheProblemSolver.nl.