Posted by: Cirilo Meggiolaro | 03/10/2009

Tip of the day #147 – Exposing a WWF workflow as a web service

I have worked with Windows Workflow Foundation (WWF) for a while now and sometimes I had to expose the workflow as a web service. It’s simple. So, let’s take a look on how you can create a simple workflow and expose it as a web service.

The process is divided in two: create an workflow that is compliant with the rules needed to expose it as a web service and the task of actually expose it.

The workflow activities that are specific for this purpose are:

  • WebServiceInput: This activity allows a workflow to act as a web method as the same way the web services you are used to develop. Once the web method is requested by the client application, the workflow will start running;
  • WebServiceOutput: The WebServiceOutput activity acts as the response for a web service. So when you create a workflow that is going to be exposed as a web service you have the request (WebServiceInput) and the response (WebServiceOutput). It is mandatory to have both activities to make a workflow execute properly and the output cannot run before the input has finished its task;
  • WebServiceFault: Returns a SOAPException object containing an inner exception defined by your code. It must be used only with a request-response operation.

How to…

On our example we are going to create a workflow that based on a product ID passed as a parameter, it’s going to retrieve a custom object from Product type.

  • On Visual Studio, create a Sequential Workflow Library project named ProductsWorkflow;
  • Rename the empty workflow that has been added to the project to ProductWorkflow.cs;
  • Drag a WebServiceInput activity onto the workflow surface and rename it to productWSInput;
  • An interface must me created to acts as the contract for our operation. So, create a new interface and name it IProduct. We are going to define a method that retrieves the unit price of a product based on the product id that will be passed from the client application as parameter. The following code shows that:

    public interface IProduct
    {
        decimal GetUnitPrice(int productID);
    }

  • Once we have the interface for our operation, let’s inform the activity what is the interface to use. Select the activity on the designer and on the Properties Window click on the browse button beside the InterfaceType property. A dialog to select the interface is displayed. From the list on the right side, select the interface we’ve just created and click OK. The following picture shows the dialog:
Picture 1 - Interface Type selection.

Picture 1 - Interface Type selection.

  • After you have selected the InterfaceType, select the method the workflow will handle by selecting the GetUnitPrice method from the MethodName property;
  • Set IsActivating property to true so when the web method is requested the workflow starts its execution;
  • Since the workflow represents the GetUnitPrice method we need to bind a property to the activity that will be the productID input parameter. To achieve that, click on the browser button besides the productID property. Click on the Bind to a new member tab, type ProductID as the member name. Make sure that the Create Property option is selected and click OK;
Picture 2 - Property binding.

Picture 2 - Property binding.

  • The properties for the WebServiceInputActivity will be similar to the following:
Picture 3 - WebServiceInput activity properties.

Picture 3 - WebServiceInput activity properties.

  • Drag a IfElseActivity onto the workflow surface. The condition that determines if either the left branch is called or the right one based on the product ID;
  • To add the condition select the left branch and on the properties window, select Code Condition on the Condition property;
  • Expand the Condition property and on the Condition property type ValidateProductID. An event handler is generated;
  • Back to the designer drag a code activity to the left branch and rename it to GetUnitPriceActivity;
  • Double-click on the activity you have just created to generate the event handler;
  • Drag an instance of the WebServiceOutputActivity just below the code activity, inside the left branch. Rename it to productWSOutput;
  • The next step is to link both input and output activities. With the output activity still highlighted set the InputActivityName to productWSInput;
  • Now let’s create a property that will hold the return value. Click on the browse button besides the ReturnValue property; Using the same steps we took to create the ProductID property, create a new one called ReturnValue;
  • To finish our workflow designer, drag a WebServiceFault activity to the right branch that will be executed if the product id is less or equals zero and name it productWSProductIDFault;
  • The properties we need to set are the InputActivityName that will be the same we used for the output activity: productWSInput and the Fault property that we need to create a new property for that. Click on the browse button besides the Fault property, click on Bind to a new member tab, type ProductFault as the member name. Make sure that the Create Property option is selected and click OK;
  • The workflow has the following appearance:
Picture 4 - Workflow designer.

Picture 4 - Workflow designer.

  • Open the code file by pressing F7 on the keyboard or right-click the designer and select View Code from the context menu;
  • We have two event handlers: ValidateProductID that is responsible for checking the product ID passed as parameter and GetUnitPriceActivity_ExecuteCode that is the one responsible for invoking the methods needed to get the product unit price;
  • Starting from the ValidateProductID let’s check if the productID is greater than zero. If true, set the e.Result property to true. Otherwise, define a new exception and store it on the ProductFault property and set the e.Result property to false. Our validation method is ready and code must be similar to the following:

    private void ValidateProductID(object sender, ConditionalEventArgs e)
    {
        if (this.ProductID > 0)
        {
            e.Result = true;
        }
        else
        {
            ProductFault = new Exception(“The product ID must be greater than zero.”);
            e.Result = false;
        }
    }

  • Now from our GetUnitPriceActivity_ExecuteCode event let’s return a symbolic value just for example purposes. Define the ReturnValue property to a decimal representing 99.99. Your workflow is ready! Now it’s time to expose it as a web service;

    ReturnValue = 99.99M;

  • Right-click on the ProductsWorkflow project and select Publish as web service. Visual Studio is going to create a web site containing an asmx file, the web.config file and a bin folder with the assembly from the Workflow library we created. The asmx name is huge. If you want to rename it, that is the right time to do it;
  • From now on the process is exactly the same than any other web service. I am using a console application project under the same solution to test it. So, right-click on the project name and select Add Web Reference from the context menu or click Add Service Reference > Advanced > Add Web Reference;
  • From the Add Web Reference dialog, click on Web services in this solution link;
  • A list of all web services will be displayed. On our example, just one service will be displayed. Click on it;
  • Type a meaningful web reference name and click Add Reference;
  • Now it is just a matter of creating an instance of the web service and invoke the GetUnitPrice method;

    ProductWorkflow_WebService myWorkflowWS = new ProductWorkflow_WebService();
    Console.WriteLine(myWorkflowWS.GetUnitPrice(100).ToString());     Console.WriteLine(myWorkflowWS.GetUnitPrice(0).ToString());

    Output:

    99.99
    System.Web.Services.Protocols.SoapException:
    System.Exception: The product ID must be greater than zero.

    […]


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Categories

%d bloggers like this: