Web Service Workflows

Deploy Distributed Business Processes With Windows Workflow And Web Services

Israel Hilerio

This article is based on a prerelease version of the .NET Framework 3.0. All information contained herein is subject to change.

This article discusses:

  • Using workflow activities to expose interaction
  • Publishing a workflow as a runtime service
  • Integrating Web services into workflows
  • Integration with Windows Communication Foundation
This article uses the following technologies:
.NET Framework 3.0, ASP.NET

Code download available at:WebServiceWorkflows2006_10.exe(521 KB)

Contents

Workflow Activities
Runtime Services
Using a Workflow
Windows Communication Foundation
Conclusion

Workflows provide a mechanism for modeling and implementing a business process as a series of activities that manage the interaction between people and back-end systems to carry out a business function. Business processes (or business applications) that require intense human involvement normally are executed over long periods of time due to the complex coordination of multiple entities. Using workflows to implement such business processes provides a mechanism for coordinating, aggregating, and routing business data.

Due to the distributed nature of a business process—coordinating information coming from multiple sources across an organization—it makes sense for a workflow to be deployed as a distributed application. To support this functionality, the Windows® Workflow Foundation team chose Web services and IIS as the host platform for executing distributed workflow applications in early versions of the .NET Framework 3.0 prerelease. This will be extended in the near future to natively support Windows Communication Foundation.

Web service definitions are designed to abstract how applications communicate with each other. They are used to model the types of interactions a workflow requires in order to carry out a business function. Using Web services, you are able to implement these interactions as distributed interoperable services that support application-to-application communications in a standard fashion. These services allow the decoupling of business logic from client application code. You can use this mechanism to generalize and publish service implementations and have them consumed by multiple clients.

Combining these two technologies provides the ability to expose business processes as Web services. This allows the creation of macro Web services that define business interactions, utilize workflow hydration and dehydration policies to support stateful Web services, and provide tracking support to enable execution transparency. Another advantage is the ability to schedule work to be executed after the completion of a Web service request. This allows a Web service request to continue executing without a direct request from a Web service client at some predefined time. Scenarios like escalation and timeout durations are supported by this functionality. In addition, the declarative nature of the workflow definition allows for the implementation of fully declarative business logic.

This article will show how you can implement long-running and stateful business processes using Windows Workflow Foundation and expose them as ASP.NET Web services. The workflow runtime will be hosted by ASP.NET and the long-running workflow would be able to continue to execute past a Web service invocation by using job scheduling constructs that are built into Windows Workflow Foundation. With this capability, business processes are able to notify users of escalation events that are affecting the normal execution flow of a process.

In this article, I will first review the declarative modeling technology of Windows Workflow Foundation. Second, I will review Web services and show how business processes implemented using Windows Workflow Foundation workflows can be exposed as Web services. Finally, I will discuss an expense reporting example that is modeled as a workflow and hosted in a Web service. By the end, you should have a good understanding of how to expose business processes as Web services using declaratively modeled workflows.

Workflow Activities

Windows Workflow Foundation uses Web services to expose interaction points modeled inside the workflow as Web service methods. Interaction points are implemented using Web service activities, which represent the functionality of the interface definitions on which they are based. There are three Web service activities, as shown in Figure 1.

Figure 1 Three Web Service Activities

Activity Name Activity Designer Functionality
WebServiceInputActivity  Provides a mechanism for receiving input from a Web service method call.
WebServiceOutputActivity  Provides a mechanism for sending output as the return value from a Web service method call.
WebServiceFaultActivity  Provides a mechanism for sending errors as the result of the invocation of a Web service method call.

These activities are mapped directly to methods defined on interfaces. They identify the input and output boundaries of a Web service inside the workflow model. When WebServiceInputActivity is executed, the input defined by the interface method it represents is available to the workflow for manipulation or it can be bound to properties in the workflow. A WebServiceOutputActivity is required to follow a WebServiceInputActivity for those interface methods that contain a return value. This activity doesn't terminate the execution of the workflow instance. Multiple Web service activities can be modeled inside a workflow definition. However, the very first activity needs to be defined as the request responsible for activating a workflow instance.

WebServiceFaultActivity is used to send an error condition back to the Web service method invocation. Once a WebServiceInputActivity is processed by a WebServiceOutputActivity or a WebServiceFaultActivity, it cannot be invoked again unless the workflow instance is repositioned back to that activity. This can be done using various loop constructs inside a Sequential Workflow or a SetStateActivity inside a State Machine Workflow.

Job scheduling inside a workflow can be accomplished using Delay activities. Delay activities work by registering a timer event with the Scheduler Service for the duration specified in the TimeSpan property of the DelayActivity. The execution of the Delay activity takes place inside the workflow instance using the Web service request before the request is answered. However, the processing of the timer event is done in a separate thread by the ManualWorkflowSchedulerService. This allows the workflow to execute independent of any Web service requests. After the timer event is consumed by the DelayActivity, the workflow continues to execute from that point forward.

Figure 2 shows the elements of a sample workflow. The Web service input request is processed by WebServiceInputActivity. PolicyActivity1 manipulates the Web service input and constructs a result value. The result value is then returned by WebServiceOutputActivity. However, before the request thread is released by the workflow instance, it will attempt to execute as many activities as possible until it achieves an idle state. Once the workflow achieves the idle state, it will release the Web service request thread. At a later time when the timer event is raised, DelayActivity will process the event in a separate thread and will continue to execute PolicyActivity2. The separate thread is a common language runtime (CLR) thread that is allocated and managed by ManualWorkflowSchedulerService. Using a PolicyActivity, the workflow is able to execute code while supporting a completely declarative solution. This allows you to express all the business logic of Web services in a declarative fashion.

Figure 2 Sample Web Service Workflow Model

Workflow Definition
Interface Contract
using System; using System.Collections.Generic; using System.Text; namespace WorkflowLibrary1 { interface Interface1 { string Salutation(string str); } }

Configuration
The WebServiceInputActivity receives the parameter str contained by the Salutation method and it is bound to the Hello property inside the workflow.


The return value of Salutation is modeled by theWebSer­vice­OutputActivity and it is bound to the HelloReturn property inside the workflow.

Runtime Services

The last step after building the workflow with Web service activities is to publish the solution. This creates a virtual directory that is used by IIS to deploy the solution as a Web service. Behind the scenes, the workflow library is wrapped by a WorkflowWebService class. This class is responsible for transferring the Web service requests from the SOAP request to the internal workflow runtime communications mechanism. In addition, this class is responsible for instantiating a new WorkflowRuntime. The system only instantiates WorkflowRuntime on the very first Web service request. Once the WorkflowWebService class creates a workflow runtime, it is cached and reused by future Web service calls. Publishing the workflow as a Web service enables the IIS server to be used as an out-of-box application host for the workflow.

By default, when publishing a Web service, the workflow runtime is configured to use the ManualWorkflowSchedulerService, which uses the Web service request thread to execute the workflow instance. This configuration needs to be modified to allow the scheduler to create a thread that processes timer events, thus allowing Delay activities to work properly. This is done by adding the UseActiveTimers flag and setting it to true. Failure to do this would result in timer events not being processed until the next Web service request is consumed by the workflow instance. The configuration change needs to take place in the web.config file created by the publish Web service step:

<add type="System.Workflow.Runtime.Hosting. ManualWorkflowSchedulerService, System.Workflow.Runtime, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" UseActiveTimers="true"/>

In order to support long-running workflows, an additional change needs to be made to the workflow runtime configuration to allow workflow instances to be persisted into the database. Persistence allows the state of the workflows to be stored in the database after the workflow is idle. If the IIS server were to crash after the workflow was persisted, the next incoming Web service request would automatically start the workflow runtime and reload the workflow instance from where it was last saved. To enable persistence, the following lines need to be added to the web.config file:

<add type="System.Workflow.Runtime.Hosting. SqlWorkflowPersistenceService, System.Workflow.Runtime, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" ConnectionString="Initial Catalog=ASPPersistence;Data Source=localhost;Integrated Security=SSPI;" UnloadOnIdle="true" />

This configuration uses the SQLWorkflowPersistenceService provided by Windows Workflow Foundation. The database tables associated with this service are part of the Windows Workflow Foundation runtime installation. For this example, the tables are contained inside a database called ASPPersistence.

More complex business processes require the use of more than one WebServiceInputActivity, as many interaction points are required to carry out the process. In order to allow Web service client applications the ability to communicate with the same workflow instance, an HTTP module is provided. This HTTP module is automatically added as part of the standard Web services publishing step and placed inside the web.config file. To enable the reentrance of a workflow instance, the HTTP module stores and retrieves the workflow instance ID to and from a client cookie called WF_WorkflowInstanceId. After the HTTP module retrieves the workflow instance ID from the cookie, it places the ID in the current HTTP Context using the __WorkflowInstanceId__ key. If the client application needs to make multiple Web service calls to the same workflow instance and cookies cannot be enabled, then an alternate approach using HTTP or SOAP header handlers needs to be implemented by a developer.

In addition, supporting multi-user scenarios requires the ability to restrict Web service interactions to specific roles. In order to support this functionality, the Windows Workflow Foundation runtime environment leverages the ASP.NET roles provider model. This allows the restriction of WebServiceInputActivities to receiving information from a user with a specific role. To configure the ASP.NET roles provider, the information shown in Figure 3 needs to be added to the web.config file. This information configures the Web service to use the SQL roles provider contained in the aspnetdb database.

Figure 3 Role Provider Aded to Web.config

<connectionStrings> <add name="SqlServerConnection" connectionString="Integrated Security = SSPI;server=.;database=aspnetdb" /> </connectionStrings> <system.web> <roleManager enabled="true" defaultProvider="SqlProvider"> <providers> <add name="SqlProvider" connectionStringName="SqlServerConnection" applicationName="ConsoleAppSample" type="System.Web.Security.SqlRoleProvider, System.Web, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" /> </providers> </roleManager> </system.web>

Using a Workflow

Now let's look at a scenario that uses a workflow accessed over Web services. Imagine an expense approval process that is going to be implemented using a Windows Workflow Foundation workflow and exposed as a Web service for external consumption. The business process needs to support users who want to create expense reports and submit them for approval. Managers need to be able to participate in this process and approve or reject expenses. If the managers don't resolve the submitted expenses within some predefined time, the system needs to send them e-mail reminders. E-mail reminders need to be sent to the managers without forcing them to interact with the system.

The first step is to define the business process that I want expense submissions to go through. The model will use input and output Web service activities to define the interaction points between my client application and my business process. Modeling the expense approval process resulted in the Windows Workflow Foundation workflow shown in Figure 4.

Figure 4 Expense Reporting Workflow Model

Figure 4** Expense Reporting Workflow Model **(Click the image for a larger view)

After modeling the business process, I can see that there are five interaction points I need to define a Web service contract for. The first interaction returns a unique ID used by the system to track expense reports. This information is sent to the client and persisted in the server for future use. The method definition for this interaction point is defined in Figure 5 with its corresponding activity definitions.

Figure 5 CreateExpenseReportId Method Contract

string CreateExpenseReportId();




Notice that there aren't any input parameters defined for CreateExpenseReportId, but there is a return value. That means the CreateExRInput WebServiceInputActivity doesn't have any parameters and the CreateExROutput WebServiceOutputActivity has a ReturnValue property that is mapped against the return string value. PolicyActivity1 is responsible for creating the expense report ID and for setting the value that will be returned as part of the CreateExROutput WebServiceOutputActivity.

To show how a workflow instance can be reentered via a Web service, I've chosen to submit the expense report on a separate interaction point. This interaction point is used to submit a complete expense report object to the business process for reviewing. There is no return value for this interface method definition (see Figure 6). Submitting the expense report to the workflow allows the workflow to decide who needs to review the submitted report based on the amount or enables it to persist the information to a back-end system. For simplicity, the scenario I've chosen doesn't interact with a separate data store. However, most real applications won't use the workflow instance as the final destination of the expense report data. The workflow instance would only serve as a cache.

Figure 6 SubmitExpenseReport Method Contract

void SubmitExpenseReport(ExpenseReport expenseReport);

Figure 6a

Figure 6a   


Figure 6b

Figure 6b   

In this case, the SubmitExRInput WebServiceActivity takes the input parameter passed in the Web service method invocation and binds it to the Report property contained by the workflow instance. The populated report object is now available to the workflow for processing.

After the expense report is submitted, the workflow goes into an approval loop. The loop was implemented using a WhileActivity with a DeclarativeCondition, which checks for a flag that signals the reviewing of the expense report. This flag is set when a manager approves or rejects the expense report. ListenActivity is contained inside WhileActivity; ListenActivity contains an interaction point to query the information contained by the expense report, an escalation path, and a reject or approve interaction point. The escalation path sets a time limit, using a DelayActivity, defining the maximum time allowed for a manager to review an expense report. Failure to do so triggers an e-mail to the manager via PolicyActivity2. The Timer event is an internal message, so it doesn't have to be defined in the Web service's contract.

The querying interaction point is implemented by a pair of input and output Web services activities. The ReviewExRInput WebServiceInputActivity receives the Web service request and the ReviewExROutput WebServiceOutputActivity returns the submitted expense report to the client application. Since this is a straight- forward query of the workflow state, there is no need to place any type of processing between these two activities (see Figure 7).

Figure 7 RetrieveExpenseReport Method Contract

ExpenseReport RetrieveExpenseReport();

Figure 6a

Figure 7a   


Figure 7c

Figure 7c   

Figure 7b

Figure 7b   


Figure 7d

Figure 7d   

The last two interaction points contained inside ListenActivity are the reject and approval actions. These two events do not require any type of client acknowledgment. They are invoked without expecting any type of return value. Their purpose is to set the expense report status and to set the reviewing flag checked by the WhileActivity to false. The setting of these variables is done inside PolicyActivity3 and PolicyActivity4 (see Figure 8).

Figure 8 RejectExpenseReport and ApproveExpenseReport Method Contracts

void RejectExpenseReport();

Figure 8a

Figure 8a   


Figure 8c

Figure 8c   
void ApproveExpenseReport();

Figure 8b

Figure 8b   


Figure 8d

Figure 8d   

Once the expense report is approved or rejected, the workflow instance is terminated and the expense report information is stored in a back-end system via the Policy Activities. Web services can no longer execute against the same instance ID.

An interesting aspect of the services inside WhileActivity is that they can only be executed by a manager. In order to enforce this security restriction, they were configured with a manager role.

Windows Communication Foundation

I have discussed implementing Web services using the ASMX technologies supported by Windows Workflow Foundation activities. The first release of Windows Workflow Foundation won't directly provide any activities or host for deploying the newer Windows Communication Foundation services; this functionality will be provided in a future release of the framework. However, there are several ways in which Windows Communication Foundation can be used today by Windows Workflow Foundation:

  • Develop a Windows Communication Foundation service that hosts the workflow runtime and executes the workflow instance.
  • Access a Windows Communication Foundation service from a workflow definition via the InvokeWebServicesActivity.
  • Access a Windows Communication Foundation service via a custom Activity.

The first approach allows developers to use the Windows Communication Foundation service as a host process for the workflow runtime. This service is responsible for triggering the execution of the workflow when a predefined OperationContract is invoked. Using this approach allows the workflow to receive input from OperationContract as workflow parameters. Figure 9 shows an example of what the OperationContract could look like.

Figure 9 Instantiating a Workflow Instance

[ServiceContract] class WorkflowApplication { WorkflowCommunicationHandler internalWFCommunications; Guid workflowInstanceId; WorkflowRuntime workflowRuntime; [OperationContract] public void BeginWorkflowApplication(string inputVariable) { workflowRuntime = new WorkflowRuntime(); internalWFCommunications = new WorkflowCommunicationHandler(); string connectionString = "Initial Catalog=WorkflowPersistence;" + "Data Source=localhost;Integrated Security=SSPI;"; workflowRuntime.AddService( new SqlWorkflowPersistenceService(connectionString, true, new TimeSpan(0, 10, 0), new TimeSpan(0, 0, 5))); ExternalDataExchangeService externalDataService = new ExternalDataExchangeService(); workflowRuntime.AddService(externalDataService); externalDataService.AddService(internalWFCommunications); workflowRuntime.StartRuntime(); Dictionary<string, object> inputParams = new Dictionary<string, object>(); Type type = typeof(Workflow1); workflowInstanceId = Guid.NewGuid(); inputParams["inputVariable"] = inputVariable; WorkflowInstance workflowInstance = Program.workflowRuntime.CreateWorkflow(type, inputParams, workflowInstanceId); workflowInstance.Start(); } }

In order to allow the Windows Communication Foundation service to facilitate ongoing interactions with the workflow instance, other interaction points can be exposed as additional OperationContract instances. The parameters of OperationContract are passed to the workflow inside a workflow communication event. Here is an additional OperationContract for the ServiceContract shown in Figure 9:

[OperationContract] public void SendDataToWorkflowApplication(Guid workflowInstanceId, string inputVariable) { WorkflowInstance workflowInstance = workflowRuntime.GetWorkflow(workflowInstanceId); internalWFCommunications.SendRequest( workflowInstance, inputVariable); }

The second approach of invoking a Windows Communication Foundation service using the InvokeWebServicesActivity is possible due to the Windows Communication Foundation support for SOAP. Unfortunately, this approach doesn't leverage the WS-* services supported in Windows Communication Foundation. A better approach is the third one—to create a Windows Workflow Foundation Activity whose internal execution method leverages the Windows Communication Foundation ServiceProxy to invoke a Windows Communication Foundation service:

protected override ActivityExecutionStatus Execute( ActivityExecutionContext executionContext) { RentalReservationsProxy p = new RentalReservationsProxy(); this.ResultValue = p.MakeReservation(this.InputValue); p.Close(); return base.Execute(executionContext); }

The execution of this activity passes the InputValue property to the RentalReservationsProxy class. Using the proxy, the activity calls the MakeReservation OperationContract contained by the RentalReservationProxy service and returns a proxy class that abstracts the WorkflowApplication Windows Communication Foundation service. The MakeReservation invocation takes the InputValue property on the activity as an input parameter and sets the ResultValue property on the activity.

Using Windows Communication Foundation together with Windows Workflow Foundation allows workflows to expose themselves to remote clients as Windows Communication Foundation services and invoke Windows Communication Foundation services via activities.

Conclusion

You can take advantage of the rich, declarative Windows Workflow Foundation model to expose your business processes, while leveraging Web services as a deployment technology for executing them. Furthermore, Windows Communication Foundation and Windows Workflow Foundation can be utilized to publish robust WS-* compliant business applications.

Israel Hilerio is a program manager at Microsoft in the Windows Workflow Foundation team. He has 15+ years of development experience doing business applications and has a PhD in Computer Science.