Chapter 13. Creating the Business Process

WHAT'S IN THIS CHAPTER?

  • Understanding how WCF supports long-running business processes

  • Creating a declarative workflow in Visual Studio

  • Implementing service contracts in XAML and calling external services during the workflow

  • Setting up the SQLWorkflowInstanceStore

  • Working with Send and Receive activities and correlating outgoing and incoming messages

This chapter is an interactive walkthrough on how to create a business process in Visual Studio 2010 using WCF and workflow services. It starts with defining requirements for a case and shows how to develop the solution step by step. At the end of this chapter, you will have developed the business process, a host for the process, the clients, and other services that interact with this process. This is a complete example so you can test the process and see it in action.

DEFINING THE REQUIREMENTS

Let's take a simple process (see Figure 13-1). This should be a kind of proof-of-concept to learn how to develop workflow processes. It's a classic example of a business process that deals with holiday requests.

The holiday request process

Figure 13.1. The holiday request process

A company wants to expose a workflow as a service that receives a holiday request from its employees, waits for approval from a manager, and sends the request to the HR service.

This workflow is started by the request from the employee and the requester receives a reference ID for further reference. The reference ID is unique and is calculated by another service. The employee can use this reference ID to check the status of the request later. When the process starts it calls out to this service to obtain the ID and sends it as the answer to the requester. After this step the process waits for the approval (or denial) for this holiday request by a manager. The process only waits for this approval for a configurable amount of time. The manager uses another application where he/she needs to input this reference ID and approve or deny the holiday request. This action results in a call to the same instance of the already running process as a signal that it can continue. When this signal arrives at the process, it continues and sends the holiday request to another service as part of the HR application that is expecting only approved requests. This step completes the process and the employee can take his holiday. When the manager does not approve or deny the request within one week, the process sends an e-mail as a reminder and waits again for the approval or denial from the manager. This can be repeated three times. If the manager does not approve or deny within three weeks, the process ends.

SETTING UP THE SOLUTION

For this solution you need to create multiple projects in one solution. Start by creating a solution called TheHolidayRequestProcessSolution.

In this solution you need to add the following eight projects:

  • HolidayRequestDataContracts: A class library which contains the WCF data contracts needed for this solution.

  • HolidayRequestProcess: This is a workflow activity library. It contains the definition of the process for the holiday request declared as XAML. You can select this template in the workflow section of the installed templates.

  • EmployeeHolidayRequestApplication: A Windows forms application. This application is used by the employee to request the holiday and start the process.

  • CalculateReferenceIDService: A WCF service application. This web service calculates the reference ID. You can find the template in the web sections of the installed templates.

  • ManagersHolidayRequestApprovalApplication: A Windows forms application that allows approving or denying the holiday requests by filling in the reference ID.

  • HolidayRequestProcessHost: A console application that hosts the process and exposes the complete process as a service.

  • HolidayRequestActivityLibrary: Another workflow activity library that contains a reference to other services and has workflow activities for the calls to the methods in these services. These activities are used in the HolidayRequestProcess activity library.

  • ReceiveApprovedHolidayRequestsService: A console application that hosts a service that receives the approved request. This is a simulation of a real web service in the HR department.

The complete solution should look like Figure 13-2.

The HolidayRequestProcessSolution

Figure 13.2. The HolidayRequestProcessSolution

CREATING THE DATA CONTRACTS

Every solution that uses WCF starts with defining the data contracts. This is the base for defining the other parts of the solution. It's clear you need a stable set of data contracts before continuing with the rest of the development. You will create a class file in the HolidayRequestDataContracts project that contains all the needed contracts.

Four data contracts in this case are recognized:

  • A contract as input for the holiday request containing the EmployeeID, the start date, and end date of the requested holiday.

  • A contract as the return for this request containing the reference ID calculated by the external service.

  • A contract for the approval or denial of the holiday request by the manager containing the employee ID of this manager, the reference ID of the holiday request being approved, and a flag indicating whether the request was approved or denied.

  • A contract as the return for this approval. For this case it contains the start date and the end date again.

First you need to add a reference to the System.Runtime.Serialization library to the HolidayRequestDataContracts project. This allows you to use the attributes to declare classes as WCF data contracts:

Add a class file to the project, name it HolidayRequestDataContract.cs and write following code in this file.

[DataContract]
public class HolidayRequestDataContract_Input
{
    [DataMember]
    public int EmployeeID { get; set; }
    [DataMember]
    public DateTime HolidayStartDate { get; set; }
    [DataMember]
    public DateTime HolidayEndDate { get; set; }
}

[DataContract]
public class HolidayRequestDataContract_Output
{
    [DataMember]
    public int ReferenceID { get; set; }
}

[DataContract]
public class HolidayApprovalDataContract_Input
{
    [DataMember]
    public int ManagerID { get; set; }
    [DataMember]
    public int ReferenceID { get; set; }
    [DataMember]
    public ApprovedOrDeniedEnum ApprovedOrDenied { get; set; }
}

[DataContract]
public enum ApprovedOrDeniedEnum
{
    [EnumMember]
    Approved,
    [EnumMember]
    Denied
}

[DataContract]
public class HolidayApprovalDataContract_Output
{
    [DataMember]
    public int EmployeeID { get; set; }
    [DataMember]
    public DateTime HolidayStartDate { get; set; }
    [DataMember]
    public DateTime HolidayEndDate { get; set; }
}

Code snippet CreatingtheBusinessProcess.zip

CREATING THE CALCULATEREFERENCEIDSERVICE

This external service calculates the reference ID for the holiday request. Visual Studio added a service named Service1.svc, a code-behind file named Service1.svc.cs, and an interface IService1.cs. You can delete these three files by doing the following:

  • Add a new WCF service named CalculateReferenceIDService.svc (see Figure 13-3).

  • In the ICalculateReferenceIDService.cs, delete the DoWork operation generated by the template together with the OperationContract attribute.

  • Replace it with a signature for your GetNewReferenceID method.

Creating the CalculateReferenceIDservice

Figure 13.3. Creating the CalculateReferenceIDservice

[ServiceContract]
public interface ICalculateReferenceIDService
{
    [OperationContract]
    int GetNewReferenceID();
}

In the CalculateReferenceIDService.svc.cs you can also delete the DoWork method and replace it with the implementation for your GetNewReferenceID method:

For this implementation as proof-of-concept for this chapter we will not add complex logic to calculate a unique reference ID now. To make the process work we just return an integer in a somewhat hardcoded way. This allows testing the process soon. Later on you need to add the behavior to calculate a unique reference ID. This could be done based on a table in a database with an identity field.

public int GetNewReferenceID()
{
    return 42;
}

Configure the properties of this project so the service will run on a specific port instead of an auto-assigned port. This allows the service to be found by the process on exactly the same port.

Open the properties of the CalculateReferenceIDService project, switch to the Web tab, check Specific port, and enter the 9876 as the port number (see Figure 13-4). You can test if this service is exposing its metadata correctly. In solution explorer, right-click the CalculateReferenceIDService.svc file and select the View in Browser menu option.

Configuring the port

Figure 13.4. Configuring the port

This opens a browser to CalculateReferenceIDService.svc. Click the link http://localhost:9876/CalculateReferenceIDService.svc?wsdl. Now you should see the WSDL for this service. Should the browser only show the directory listing, you need to click on the link to CalculateReferenceIDService.svc first.

CREATING THE RECEIVEAPPROVEDHOLIDAYREQUESTSSERVICE

You created the ReceiveApprovedHolidayRequestService as a console application instead of a web application. This is done for testability reasons as it's easier to debug and show results to the tester of the service when it's created as a console application. In this console application, the code for opening the host and declaring the service interface and the implementation of this service interface will be present together. This makes it a self-service host. The reason you use a self-service host is to see the result of the call to the service operation in the console. This is convenient in this proof-of-concept stage but in production this could change to an IIS serviced service with the same contract and configuration.

Add the references to the System.ServiceModel and System.Runtime.Serialization libraries.

Add a class file called ReceiveApprovedHolidayRequestService.cs and add the following code:

[ServiceContract]
public interface IReceiveApprovedHolidayRequestService
{
    [OperationContract]
    void ReceiveApprovedHolidayRequest(ApprovedHolidayData approvedHolidayData);
}

public class ReceiveApprovedHolidayRequestService :
              IReceiveApprovedHolidayRequestService
{
    public void ReceiveApprovedHolidayRequest
                  (ApprovedHolidayData approvedHolidayData)
    {
      Console.WriteLine("Got Approved Holiday Request");
      Console.WriteLine(
       string.Format(" by employee {0}, approved by {1}, from  {2} to {3}",
       approvedHolidayData.EmployeeID.ToString(),
       approvedHolidayData.ApprovedByManagerID.ToString(),
       approvedHolidayData.HolidayStartDate.ToShortTimeString(),
       approvedHolidayData.HolidayEndDate.ToShortTimeString()));
    }
}

[DataContract]
public class ApprovedHolidayData
{
    [DataMember]
    public int EmployeeID { get; set; }
    [DataMember]
    public int ApprovedByManagerID { get; set; }
    [DataMember]
    public DateTime HolidayStartDate { get; set; }
    [DataMember]
    public DateTime HolidayEndDate { get; set; }
}

Code snippet CreatingtheBusinessProcess.zip

This file now contains the data contract, the service contract, and the implementation of the operation together.

Add an application configuration file to the project. This app.config file should contain following configuration:

<system.serviceModel>
<behaviors>
 <serviceBehaviors>
  <behavior name="ExposeMetadata">
   <serviceMetadata
     httpGetEnabled="true"
     httpGetUrl=
      "http://localhost:9875/ReceiveApprovedHolidayRequestsService/MEX"/>
   </behavior>
  </serviceBehaviors>
 </behaviors>
 <services>
  <service
    name=
     "ReceiveApprovedHolidayRequestsService.ReceiveApprovedHolidayRequestService"
    behaviorConfiguration="ExposeMetadata">
    <endpoint
      address="http://localhost:9875/ReceiveApprovedHolidayRequestsService"
      binding="basicHttpBinding"
bindingConfiguration=""
      contract=
       "ReceiveApprovedHolidayRequestsService.
         IReceiveApprovedHolidayRequestService" />
    </service>
  </services>
</system.serviceModel>

Code snippet CreatingtheBusinessProcess.zip

To host the service, add this code to the main method in the program.cs file:

try
{
    Console.WriteLine("HOST : ReceiveApprovedHolidayRequestsService");
    ServiceHost serviceHost;
    serviceHost = new ServiceHost(typeof(ReceiveApprovedHolidayRequestService));
    serviceHost.Open();
    Console.WriteLine("started");
}
catch (Exception ex)
{
    Console.WriteLine(ex.Message);
}
Console.ReadKey();

You can now test that this console application is exposing the WSDL file correctly. First build and then start this application outside Visual Studio. You need to start as administrator. Browse to the directory where the executable is placed, right-click it and select the Run As Administrator option.

Now open a browser and type in the URL configured in the serviceMetaData behavior of the configuration file, which is http://localhost:9875/ReceiveApprovedHolidayRequestsService/MEX.

You should see the WSDL in the browser.

ADDING SERVICE REFERENCES TO THE HOLIDAYREQUESTACTIVITYLIBRARY PROJECT

The HolidayRequestActivityLibrary project will only contain two service references to the services you just created. It will not contain anything else. So you can delete the Activity1.xaml file.

Adding service references to a workflow activity library results in the creation of reusable activities to call operations in these services. These activities become visible in the workflow designer of other workflow activity libraries that reference this project, such as your HolidayRequestProcess project. This is a good design approach and separates the activities as wrappers to service calls from the workflow itself.

This leads to reusability of activities in other business processes.

Adding service references here also generates the configuration for client endpoints in the app.config file. This configuration has no use here as this project is a library. You will see later that you need to copy this configuration to the project which is hosting the workflow process.

Adding the CalculateReferenceIDService

To add a reference to the CalculateReferenceIDService, right-click References and select Add Service Reference (see Figure 13-5). This opens the Add Service Reference dialog box.

Adding a service reference

Figure 13.5. Adding a service reference

Now click the Discover button and select Services in Solution (see Figure 13-6). This results in an overview of the interface containing the operations in this service.

Discover the services in the solution

Figure 13.6. Discover the services in the solution

You should see the GetNewReferenceID operation (see Figure 13-7). Set the namespace to CalcIDService.

Click OK and Visual Studio now generates the proxies and adds an endpoint to the configuration file.

Set the namespace to CalcIDService

Figure 13.7. Set the namespace to CalcIDService

Adding the ReceiveApprovedHolidayRequestsService

Add a second service reference to the ReceiveApprovedHolidayRequestsService. Visual Studio cannot detect the WSDL from the solution as this is a self-hosted service. You have to start the ReceiveApprovedHolidayRequestsService outside Visual Studio. Remember to do this as administrator.

The address for this service is http://localhost:9875/ReceiveApprovedHolidayRequestsService/MEX.

Use HRService as a namespace for this service reference.

Now have a look at the app.config file of the HolidayRequestActivityLibrary project. It should contain two endpoints like this:

<endpoint
    address="http://localhost:9876/CalculateReferenceIDService.svc"
    binding="basicHttpBinding"
    bindingConfiguration="BasicHttpBinding_ICalculateReferenceIDService"
    contract="ICalculateReferenceIDService"
    name="BasicHttpBinding_ICalculateReferenceIDService" />
<endpoint
    address="http://localhost:9875/ReceiveApprovedHolidayRequestsService"
    binding="basicHttpBinding"
bindingConfiguration="BasicHttpBinding_IReceiveApprovedHolidayRequestService"
    contract="IReceiveApprovedHolidayRequestService"
    name="BasicHttpBinding_IReceiveApprovedHolidayRequestService" />

It's okay to now to close the console application hosting the ReceiveApprovedHolidayRequestsService.

Developing the HolidayRequestProcess

The HolidayRequestProcess contains the definition of workflow and needs references to a number of system libraries: System.Activities, System.Runtime.Serialization, System.ServiceModel, and System.ServiceModel.Activities.

This project also needs a reference to the HolidayRequestActivityLibrary library and the HolidayRequestDataContracts library. This is needed as the first one contains the activities to call the operation in the external services (CalcIDService and HRService) and the HolidayRequestDataContracts contains the data contracts that define the input parameters and output parameters for the service operations you develop in the workflow.

In this workflow service you create two service operations, RequestHoliday and ApproveRequest, by dropping activities to the workflow designer. This is a workflow-first approach and allows you to define the service contract while developing the business process.

Adding the Workflow

Add a workflow activity to the project. You can select the template in the workflow part of the installed templates in the Add New Item dialog box (see Figure 13-8). Name this workflow activity HolidayRequestProcessDefinition.xaml.

Add a workflow activity to the project

Figure 13.8. Add a workflow activity to the project

Now to build the complete solution, open the activity, and make the toolbox visible. This toolbox should now contain two activities as wrappers to the operations in the two services — see Figure 13-9. At this moment the designer canvas is empty. Add a ReceiveAndSendReply activity from the toolbox to the designer.

The toolbox containing the two activities

Figure 13.9. The toolbox containing the two activities

You can find this activity in the Messaging section of the Toolbox. See Figure 13-10. Simply drag the activity into Drop Activity Here.

This results in two separate activities — see Figure 13-11.

  • The first one is a Receive activity that defines the incoming parameters for the operation.

  • The second one is a SendReply activity that defines the return type for the operation.

The ReceiveAndSendReply activity

Figure 13.10. The ReceiveAndSendReply activity

The activities in the designer

Figure 13.11. The activities in the designer

Creating Variables

Before configuring the activities in detail for the workflow, you need to create variables to contain the data you are getting as input from the activities and to contain the data you are sending back as answers.

A variable is also needed to contain the reference ID obtained from the external service. You also need a variable to contain the number of iterations waiting for the timeout that has been executed.

Add six variables — to create those variables, click the sequence activity to set the scope and click the Variables button in the lower-left part of the designer, as seen in Figure 13-12. Every variable needs a type. These types are either defined in the HolidayRequestDataContracts library, a type generated by the proxies in the HolidayRequestActivityLibrary library, or simply an integer for the reference ID and retryCounter.

The variables tab

Figure 13.12. The variables tab

Selecting a type for the variable is done in the Browse and Select a .NET Type dialog box. This dialog box can be opened by selecting the Browse for Types option in the Variable type column in the variables list — see Figure 13-13.

Browse for Types

Figure 13.13. Browse for Types

Create the variables named holidayApprovalInput, holidayApprovalOutput, holidayRequestInput, holidayRequestOutput, approvedHolidayInput, referenceID, and retryCounter. Select the appropriate type in the dialog box (see Figure 13-14).

Browse and Select a .Net Type dialog

Figure 13.14. Browse and Select a .Net Type dialog

The variables and their types are shown here:

  • holidayApprovalInput: HolidayApprovalDataContract_Input

  • holidayApprovalOutput: HolidayApprovalDataContract_Output

  • holidayRequestInput: HolidayRequestDataContract_Input

  • holidayRequestOutput: HolidayRequestDataContract_Output

  • referenceID: Int32

  • approvedHolidayInput: ApprovedHolidayData

  • correlationHandle: CorrelationHandle

  • retryCounter: Int32

The result should look like Figure 13-15.

All variables

Figure 13.15. All variables

Configuring the Receive Activity

Change the Operation Name to RequestHoliday — see Figure 13-16. To define the parameters for this operation, click the Define box.

The Receive activity

Figure 13.16. The Receive activity

This opens the Content Definition dialog box. Check the Parameters radio button. Click the Add New Parameter link. Name the parameter inputparam_holidayRequest. To define the type of the parameter, select the Browse for Types option in the drop-down. The type is HolidayRequestDataContract_Input in the HolidayRequestDataContracts namespace.

Back in the Content Definition dialog box, type the name of the variable for this incoming data. The name of the variable is holidayRequestInput (see Figure 13-17).

Content Definition dialog box

Figure 13.17. Content Definition dialog box

Notice that this input box supports intellisense and lets you find the name of the variable easily.

Close the Content Definition dialog box.

Back in the designer, open the property window of the Receive activity and check the CanCreateInstance property. This indicates that calling this operation creates a new instance of the workflow. See Figure 13-18.

Check the CanCreateInstance flag

Figure 13.18. Check the CanCreateInstance flag

Configuring the Send Activity

Before configuring the Send activity, you need to add a correlation variable. This variable is used in the configuration of the SendReply activity and later in the Receive activity for the ApproveRequest operation. In the designer, activate the sequence again to set the scope and open the variables list.

Add a variable called correlationHandle and set the type to CorrelationHandle. See Figure 13-19. Now click the define link in the Content input box of the Send activity.

Select correlationHandle as type

Figure 13.19. Select correlationHandle as type

This opens the Content Definition window. In this window select parameters, add a new parameter called outputparam_holidayRequest, and set the type to HolidayRequestDataContract_Output by using the Browse for Types option — which opens the Select a .NET Type dialog box. Set the value to the holidayRequestOutput variable. In the designer, open the property window of the Send activity, and open the Add Correlation Initializers window by clicking the ellipsis button of the CorrelationInitializers property (see Figure 13-20).

The SendReply properties

Figure 13.20. The SendReply properties

In this dialog box, click the Add Initializer link and type in correlationHandler. This is the name of the variable you created to correlate the Send activity to the Receive activity of the ApproveRequest operation. Leave the drop-down list on Query correlation initializer and open the drop-down under XPATH Queries. In this drop-down, select ReferenceID from the outputparam_holidayRequest parameter. See Figure 13-21. Call to the CalcIDService.

Between the Receive and the Send Activities, you need to call the CalcIDService to obtain the reference ID and you need to assign the value of it to the return value of the operation.

Drag and drop the GetNewReferenceID activity from the toolbox between the Receive and the Send Activities.

The correlation initializer

Figure 13.21. The correlation initializer

Open the property window of this activity and click the ellipsis button of the GetNewReferenceIDResult property. This opens the Expression Editor. In this editor, enter referenceID. This is the name of the variable that holds the value of the referenceID — see Figure 13-22. Now you need to assign the obtained referenceID to the output parameter for the Send activity. For this you need to drop two Assign activities between the GetNewReferenceID and the SendActivity activities. The Assign activity can be found in the toolbox in the Primitives part. The first Assign activity instantiates the variable holidayRequestOutput and the second Assign activity sets the referenceID to the output value of the GetNewReferenceID activity.

Open the Properties window of the first Assign activity and configure these properties (see Figure 13-23):

  • To: holidayRequestOutput

  • Value: New HolidayRequestDataContract_Output()

Expression Editor

Figure 13.22. Expression Editor

Properties of the Assign activity

Figure 13.23. Properties of the Assign activity

Open the Properties window for the second Assign activity and configure these (see Figure 13-24):

  • To: holidayRequestOutput.ReferenceID

  • Value: referenceID

We add a WriteLine activity after the Send activity to see when the RequestHoliday operation is called and what the referenceID from the service is. You can drag the WriteLine activity from the Primitives part in the toolbox — see Figure 13-25.

Properties of the Assign activity

Figure 13.24. Properties of the Assign activity

The WriteLine activity in the toolbox

Figure 13.25. The WriteLine activity in the toolbox

This WriteLine activity is here for testing purposes. The activity writes a string of data to the console application which is hosting this workflow service. You can delete this activity when the workflow is compiled for production.

Open the Properties of this activity and configure the text so it contains a formatted string displaying the obtained referenceID. You can configure the text in the Expression Editor after clicking the ellipsis button of the text property. Add this code in the editor — see Figure 13-26.

String.Format("Received Request {0}", referenceID)

The sequence should now look like Figure 13-27.

The expression editor

Figure 13.26. The expression editor

The Complete sequence

Figure 13.27. The Complete sequence

Now add a while activity as the next step in the process. You can find the while activity in the Control Flow part of the toolbox.

This while activity allows the steps to be defined and repeated while a condition is true. This condition is based on the retryCounter variable. Only if the retryCounter is less than three, will the steps be repeated. The retryCounter will be incremented or changed by the activities in the while activities.

Open the Properties of the while activity and set the value of the condition property to a Boolean Condition. Specify that the retryCounter should be less than 5 (see Figure 13-28).

Use this condition:

retryCounter < 3
The condition

Figure 13.28. The condition

The next step is to add a Pick activity in the body of the while activity. A Pick activity allows the process to wait for a trigger before continuing. In this case the process needs to wait for either a timeout configured by a Delay activity or the received approval or denial of the holiday request from the managers.

This Pick activity can be found in the Control Flow section of the toolbar.

Now drag a Delay activity to the Trigger area in the left branch of this Pick activity (see Figure 13-29).

The Pick activity in the designer

Figure 13.29. The Pick activity in the designer

Open the Properties window of the Delay activity and configure the duration property by clicking the ellipsis button. The duration is defined as a VB.NET expression. For the first test, use a duration of 30 seconds by entering System.TimeSpan.FromSeconds(30).

In production you should set this to a higher delay expressed in weeks — see Figure 13-30.

In the action area of the right branch of the Pick activity, below the delay, add a Sequence activity. This activity contains two activities. The first is a WriteLine activity to write the console that the delay has expired. This is temporary and only there to see how the delay works. The second one is an Assign activity to increment the retryCounter variable.

Add a WriteLine activity and set the text to delayed. Add an Assign activity and set the To property to retryCounter and the Value property to retryCounter+1. See Figure 13-31.

Defining the delay

Figure 13.30. Defining the delay

Properties of the Assign activity

Figure 13.31. Properties of the Assign activity

Configuring the ApproveRequest ReceiveAndSendReply Activity

In the Trigger area of the sequence in the right branch of the Pick activity, drop another ReceiveAndSendReply activity. See Figure 13-32. This will create the second operation in the workflow service and will receive the approval or denial of the holiday request. This operation is called by the ManagersHolidayRequestApproval application.

Now set the OperationName to ApproveRequest. Click the Define link to set the content of the Receive activity. In the Content Definition window, check the Parameters radio button. Add a parameter called inputparam_holidayApproval, set the type to HolidayRequestDataContract_input, and assign this parameter to the holidayApprovalInput variable. See Figure 13-33.

Configure the correlation by setting the CorrelatesWith to correlationHandle.

How the Pick activity looks now

Figure 13.32. How the Pick activity looks now

Assign the parameter

Figure 13.33. Assign the parameter

Open the CorrelatesOn Definition window by clicking the ellipsis button of the CorrelatesOn property. In the XPath Queries drop-down, select ReferenceID as the parameter to correlate. See Figure 13-34.

Configuring the correlation

Figure 13.34. Configuring the correlation

Configure the SendReply activity. Open the Content Definition window, select Parameters, and add a parameter called outputparam_holidayApproval. Set the type to HolidayApprovalDataContract_Output, found in the HolidayRequestDataContracts, and assign this to the holidayApprovalOutput variable. See Figure 13-35.

Content definition window

Figure 13.35. Content definition window

In-between the Receive activity and the SendReply activity, drop two Assign activities. The first Assign activity instantiates the output variable. The second Assign activity sets the EmployeeID as a property of this variable. It is set to the EmployeeID which is received as input for the approval request:

holidayApprovalOutput                New HolidayApprovalDataContract_Output()
holidayApprovalOutput.EmployeeID     holidayRequestInput.EmployeeID

After the SendReply activity, add an Assign activity that sets the retryCounter variable to 99. The result should look like Figure 13-36.

This is a value higher than the value used in the condition of the while activity and will stop the while activity (see Figure 13-37). In the Action part of the right branch of the Pick activity, add an If activity. You can find this in the Control Flow Section of the toolbox. This activity will check the holidayApprovalInput variable to see whether it was approved or denied.

The assign statements

Figure 13.36. The assign statements

Set the retryCounter

Figure 13.37. Set the retryCounter

Change the DisplayName to ApprovedOrDenied. Edit the condition and enter the following condition:

holidayApprovalInput.ApprovedOrDenied = ApprovedOrDeniedEnum.Approved

See Figure 13-38 for the result.

The condition in the Expression Editor

Figure 13.38. The condition in the Expression Editor

In the Then branch of the If activity, drop a Sequence activity to group all needed activities when the holiday request is approved. Also drop a Sequence activity in the Else branch.

Add the ReceiveApprovedHolidayRequest activity from the toolbox to the left Sequence. The call to the HRService only executes when the holiday request is approved.

Set the approvedHolidayData property of this activity to the approvedHolidayInput variable. See Figure 13-39.

Set the approvedHoliday property

Figure 13.39. Set the approvedHoliday property

Add these five assigned activities to the action part before the ReceiveApprovedHolidayRequest activity. These activities construct the approvedHolidayInput variable. Configure the To and Value properties for these activities as follows:

To: approvedHolidayInput
Value: New HolidayRequestActivityLibrary.HRService.ApprovedHolidayData

To: approvedHolidayInput.ApprovedByManagerID
Value: holidayApprovalInput.ManagerID

To: approvedHolidayInput.EmployeeID
Value: holidayRequestInput.EmployeeID

To: approvedHolidayInput.HolidayStartDate
Value: holidayRequestInput.HolidayStartDate

To: approvedHolidayInput.HolidayEndDate
Value: holidayRequestInput.HolidayEndDate

See Figure 13-40.

The assign statements

Figure 13.40. The assign statements

Add a WriteLine activity to each of the branches indicating what the result of the managers is to the console screen.

DEVELOPING THE HOLIDAYREQUESTHOST

In the HolidayRequestHost, add references to HolidayRequestActivityLibrary, HolidayDataContracts, and HolidayRequestProcess.

The console application also needs references to System.Activities, System.ServiceModel, System.Runtime.Serialization, System.ServiceModel.Activities, and System.ServiceProcess (see Figure 13-41).

The references needed for the Host

Figure 13.41. The references needed for the Host

Add an application configuration file to the project. Copy/Paste the complete content of the app.config file of the HolidayRequestActivityLibrary project into this configuration file.

In the program.cs file, add code to the main method to instantiate a WorkflowServiceHost; add a SqlWorkflowInstanceStoreBehavior to the WorkflowServiceHost; add ServiceMetadataBehavior; and then set the timeToUnload interval to 0 seconds:

try
{
  WorkflowServiceHost workflowServiceHost;
  workflowServiceHost = new WorkflowServiceHost(
   new HolidayRequestProcess.HolidayRequestProcessDefinition(),
   new Uri(@"http://localhost:9874/HolidayRequestProcess"));

  workflowServiceHost.Description.Behaviors.Add(
   new SqlWorkflowInstanceStoreBehavior(
      "Data Source=.;
       Initial Catalog=SqlWorkflowInstanceStore;Integrated Security=True"));

  ServiceMetadataBehavior serviceMetadataBehavior;
  serviceMetadataBehavior = new ServiceMetadataBehavior();
  serviceMetadataBehavior.HttpGetEnabled = true;
  workflowServiceHost.Description.Behaviors.Add(serviceMetadataBehavior);

  WorkflowIdleBehavior workflowIdleBehavior = new WorkflowIdleBehavior()
  {
      TimeToUnload = TimeSpan.FromSeconds(0)
  };
  workflowServiceHost.Description.Behaviors.Add(workflowIdleBehavior);

  workflowServiceHost.Description.Behaviors.Find<ServiceDebugBehavior>().
   IncludeExceptionDetailInFaults = true;

  workflowServiceHost.Open();

  Console.WriteLine("WorkflowServiceHost started.");

}
catch (Exception ex)
{
  Console.WriteLine(ex.Message);
  if (ex.InnerException != null)
  {
    Console.WriteLine(ex.InnerException.Message);
  }
}

Console.ReadKey();

Code snippet CreatingtheBusinessProcess.zip

TESTING TO SEE IF THIS SERVICE HOST EXPOSES THE METADATA CORRECTLY

Start the HolidayRequestProcessHost application outside Visual Studio. Make sure you start it as administrator. Open a browser and browse to http://localhost:9874/HolidayRequestProcess. This should show a page with a link to http://localhost:9874/HolidayRequestProcess?wsdl. See Figure 13-42. Develop the EmployeeHolidayRequestApplication application.

The WSDL of the HolidayRequestProcess

Figure 13.42. The WSDL of the HolidayRequestProcess

While the HolidayRequestProcessHost is running outside Visual Studio, open the EmployeeHolidayRequestApplication and add a service reference to the HolidayRequestProcess service. In the Add Service Reference window, set the address to http://localhost:9874/HolidayRequestProcess?wsdl and set the namespace to HolidayRequestService. See Figure 13-43.

Adding a service reference to the HolidayRequestProcess service

Figure 13.43. Adding a service reference to the HolidayRequestProcess service

Add a button to the form and write the following code as code-behind:

HolidayRequestService.ServiceClient client;
client = new HolidayRequestService.ServiceClient();
HolidayRequestService.HolidayRequestDataContract_Output output;
output = client.RequestHoliday(
  new HolidayRequestService.HolidayRequestDataContract_Input()
    { EmployeeID = 101,
      HolidayEndDate = System.DateTime.Now,
      HolidayStartDate = System.DateTime.Now
    });
MessageBox.Show(output.ReferenceID.ToString());

This is not really the final code, just a test client as part of the proof-of-concept. In real life, the EmployeeHolidayRequestApplication would have a more complex interface. But this application allows us to test the workflow. The code just calls the RequestHoliday method of the proxy which starts the workflow you developed and is hosted by the workflowservicehost in the HolidayRequestProcessHost. The result of the call, the received EmployeeID, is shown in a message box.

DEVELOPING THE MANAGERSHOLIDAYREQUESTAPPROVALAPPLICATION

This application is the test application where a manager approves or denies the holiday request. Again this is a test application and would have a more complex user interface in real life.

Add a reference to the HolidayRequestProcess service as you did for the employee application. In the Add Service Reference dialog, use http://localhost:9874/HolidayRequestProcess as the address and type in HolidayRequestService as the namespace for the service.

Add a button and a textbox to the form. The textbox is needed for the referenceID. For the button, add the following code:

HolidayRequestService.ServiceClient client;
client = new HolidayRequestService.ServiceClient();
HolidayRequestService.HolidayApprovalDataContract_Input input;
input = new HolidayRequestService.HolidayApprovalDataContract_Input();
input.ManagerID = 1;
input.ReferenceID = int.Parse(textBox2.Text);

HolidayRequestService.HolidayApprovalDataContract_Output output;
output = client.ApproveRequest(input);

MessageBox.Show(output.EmployeeID.ToString());

CREATING THE SQLWORKFLOWINSTANCESTORE

You need to create a SQL database to store the state of the process. This SQL database can be running under SQL Express or SQL Server. This database needs to contain the appropriate tables and stored procedures so the workflowservicehost can store and retrieve the state of the process. The name of the database must be specified in the connectionstring you gave to the SqlWorkflowInstanceStoreBehavior you added to the WorkflowServiceHost.

To create the needed tables and stored procedures, you need to run two scripts on the database, which can be found in the C:WindowsMicrosoft.NETFrameworkv4.0.30128SQLen. directory. The scripts are SqlWorkflowInstanceStoreLogic.sql and SqlWorkflowInstanceStoreSchema.sql.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.15.31.22