Hour 5 Creating an Escalation Workflow

What You’ll Learn in This Hour:

Image   Accessing a workflow from two different forms (hosts)

Image   Retrieving workflows from the SqlWorkflowPersistence service

Image   Configuring a workflow to be tracked

Image   Configuring a workflow to be persisted in multiple hosts

Image   Using the Listen activity to wait for events with a timeout provision

Image   Using the SqlTrackingQuery type to extract tracking information

Image   Using the WorkflowMonitor SDK sample to view and track information

This application builds on an application very similar to the one you completed in the previous hour. It adds an option to require second level approval when an order amount falls between the level 1 approval threshold and the denial threshold. An escalation form is created that retrieves workflows awaiting second level approval and then allows for final approval or rejection.

The Listen activity is also introduced. The Listen activity powers the seminal workflow pattern, where the workflow awaits one or more events and times out if the events do not arrive in time.

Finally, you learn how to configure hosts to track workflows via the SqlTrackingService. Tracking allows running workflow information to be emitted and saved in a storage medium. You then retrieve the tracking information using the SqlTrackingQuery type and the WorkflowMonitor SDK sample.

From a tactical standpoint, you will modify an existing solution that is very similar to the one you ended with in the previous hour. It contains an escalation form and an order amount that is used to test approval, in contrast to using the last digit of the order number, which was done in the previous hour. Here is a summary of the steps you will perform in this hour in the order they are performed:

1.   Add a new method that requests more information to the existing local service and approval form.

2.   Create a new local service to support the escalation.

3.   Create a new event args payload class that carries level-two approval or rejection.

4.   Extend the workflow to support escalation.

5.   Modify the escalation form to implement the level-two approval and rejection events and to retrieve a persisted workflow.

6.   Add persistence to both hosts.

7.   Add tracking to both hosts.

8.   Use the SqlTrackingQuery type to retrieve tracking information.

9.   Use the WorkflowMonitor SDK sample to graphically view tracking information.

Image

Creating a Windows Service WF runtime host and accessing it from both forms would be a better architecture for many scenarios. This approach was not taken for simplicity’s sake. An example of hosting the WF runtime in a Windows Service, however, is provided on my blog at www.reassociate.net.

Updating the Local Services

You need to add an additional method to the local service used in the previous hour that requests more information when the threshold falls in between approval and rejection. You also need to create an entirely new interface that processes the second-level approval and rejection.

Updating the Basic Local Service Interface

You need to add the method to the local services that requests additional approval in the next steps.

1.   Open the Order Escalation Solution in

     C:SamsWf24hrsHourHour05EscalationWorkflowLab1OrderEscalationSolution.

2.   Add the following code to the BasicLocalService.cs file below the line that contains void Reject (in the OrderEscalationLocalServices project).

        void MoreInfo(string status, string orderNumber);

Creating the Escalation Local Service

The escalation events (second-level approval and rejection) are monitored from the same workflow but are fired by a different form, the escalation form. Therefore, a separate local service interface is created for them that will in turn be implemented in the escalation form. You do so in the next steps.

1.   Add a new class to the OrderEscalationLocalServices project and name it LocalServiceEscalation.cs.

2.   Replace the contents of the class file with the following:

using System;
using System.Collections.Generic;
using System.Text;
using System.Workflow.Runtime;
using System.Workflow.Activities;

namespace SAMS24WFBook
{
    [ExternalDataExchange]
    public interface ILocalServiceEscalation
    {
        event EventHandler<OrderLevel2EventArgs>
OrderWorkflowLevel2Approval;
        event EventHandler<OrderLevel2EventArgs>
OrderWorkflowLevel2Rejection;
    }
}

3.   Add another class to the project named LocalServiceEscalationEventArgs.cs and replace its content with the following:

        using System;
        using System.Collections.Generic;
        using System.Text;
        using System.Workflow.Activities;

        namespace SAMS24WFBook
        {
           [Serializable]
           public class OrderLevel2EventArgs : ExternalDataEventArgs
           {
              private string orderApprovalStatus;
              public string OrderApprovalStatus

              {
                 get { return orderApprovalStatus; }
                 set { orderApprovalStatus = value; }
              }
              public OrderLevel2EventArgs(Guid instanceId, string status)
                 : base(instanceId)
                 {
                    orderApprovalStatus = status;
                 }
              }
           }

4.   Right-click the OrderEscalationLocalServices project, select Build, and fix errors, if any (do not build the entire solution yet).

Extending the Workflow

You will add the additional activities to permit additional approval to be requested and responded to. Figure 5.1 identifies the completed workflow. The highlighted section is the portion you will add in this hour.

FIGURE 5.1 Completed workflow.

Completed workflow.

Adding the Activities

In this section, you will work with two new activities: the Listen and EventDriven activities. Listen activities underpin the seminal workflow pattern to wait for one or more events and to time out if an event is not received in time. The Listen activity, like the IfElse activity, contains one or more branches that each holds one or more activities themselves. The Listen activity and the IfElse activities look nearly identical when viewed in the workflow designer. The difference is that Listen activity branches await external input. For instance, in our sample, one branch will await approval, another rejection, and the final will time out if approval is not received in time. HandleExternalEvent activities wait for the events and a Delay activity supplies the capability to set up a timer.

Like an IfElse branch, multiple activities can also be placed in each Listen activity branch. There is, though, one restriction enforced by Listen activity branches not done so by IfElse branches. Remember that Listen activity branches are supposed to listen for an external event or wait for a timeout. To accommodate this, Listen activity branches require that the first activity placed in them be a blocking activity. Blocking activities include HandleExternalEvent and Delay activities that you have worked with, a WebServiceInput activity used with web services, and a Receive activity used with Windows Communication Foundation. The Listen activity branch activities are called EventDriven activities. EventDriven activities are not only used in Listen activities. They underpin state machine workflows as well.

Image

You can create your own blocking activities. One way to do so is to create custom activities that derive from IEventDriven. The second way is to use WCA.exe to create strongly typed HandleExternalMethod activities (see Hour 10, “Working with EventHandlingScope and Strongly Typed Activities”).

In the next steps you begin updating the workflow.

1.   Open the OrderEscalationWorkflow in the workflow designer.

2.   Add a third branch to the CheckCredit IfElse activity by right-clicking on the portion of the CheckCredit activity that surrounds the branches and selecting Add Branch.

3.   Rename the Rejected branch MoreInfo (not the branch you just added).

4.   Rename the new branch you just added Rejected.

5.   Move the CallExternalMethod activity from the MoreInfo branch to the Rejected branch (this moves the activity that performs rejection into the right rejection branch).

6.   The CheckCredit activity should now look like Figure 5.2.

FIGURE 5.2 CreditCheck with unconfigured MoreInfo branch.

CreditCheck with unconfigured MoreInfo branch.

7.   Add a CallExternalMethod activity to the newly added (MoreInfo) IfElse branch.

8.   Now add a Listen activity below the CallExternalMethod activity in the MoreInfo branch and name it WaitForResponse.

9.   Add a third branch to the Listen activity by right-clicking the portion of the activity that surrounds the branches and selecting Add Branch.

10.   Name the left branch ApprovalReceived and add a HandleExternalEvent activity to it.

11.   Name the middle branch RejectionReceived and add a HandleExternalEvent activity to it.

12.   Name the right branch TimeOut and add a Delay and Code activity in that order. If you tried to add the Code activity first, the red exclamation mark signifying an error would remain because a blocking activity must be the first child.

13.   The CheckCredit activity and its child WaitForResponse activity should look like Figure 5.3.

FIGURE 5.3 Workflow with configured CheckCredit activity.

Workflow with configured CheckCredit activity.

Image

The Parallel activity covered in Hour 8, “Working with Parallel Activities and Correlation,” and Hour 9, “Working with the Replicator and While Activities,” also closely resembles the Listen activity. Many confuse it with the Listen activity and create workflows that are terribly processor intensive and at worst bring servers down. The Parallel activity runs its branches concurrently. Therefore, if a HandleExternalEvent activity exists in one branch and a Delay activity exists in another, it will keep executing both branches. Use the Parallel activity if you really want to perform two or more tasks concurrently. Use the Listen activity if you want to wait for and then act on the receipt of an event.

Configuring CheckCredit’s Activities

You configure the CheckCredit activity’s child activities in this section’s subsections.

Configure the MoreInfo CallExternalMethod Activity

Configure the MoreInfo CallExternalMethod activity in the next steps.

1.   Click the CallExternalMethod activity in the MoreInfo branch of the CheckCredit activity.

2.   Click the ellipsis in its InterfaceType property, select BasicLocalService, select ILocalService in the middle of the screen, and click OK. Be careful because there are two interfaces to select from.

3.   Select the MoreInfo method in the MethodName property.

4.   Enter AdditionalApproval in the MethodInvoking property, press Enter, and enter the following in its handler:

       Status = "SecondLevelApproval";

5.   Switch back to the workflow designer.

6.   Click the ellipsis in the orderNumber property and select the OrderNumber member to bind the orderNumber property to the OrderNumber member.

7.   Click the ellipsis in the status property and select the Status member to bind the status property to the Status member.

Configure the ApprovalReceived HandleExternalEvent Activity

In the next steps, you configure the ApprovalReceived HandleExternalEvent activity.

1.   Click the HandleExternalEvent activity in the ApprovalReceived branch of the WaitForResponse activity.

2.   Click the ellipsis in its InterfaceType property, click the +, select BasicLocalService, select ILocalServiceEscalation in the middle of the screen, and click OK.

3.   Select the OrderWorkflowLevel2Approval event in the EventName property.

4.   Enter ApprovalLevel2Invoked in the Invoked property, press Enter, and enter the following in its handler:

        MessageBox.Show("Level 2 approval for order: " + OrderNumber);

Configuring the RejectionReceived HandleExternalEvent Activity

In the next steps, you configure the RejectionReceived HandleExternalEvent activity.

1.   Switch back to the workflow designer.

2.   Click the HandleExternalEvent activity in the RejectionReceived branch of the Listen activity.

3.   Click the ellipsis in its InterfaceType property, click the +, select BasicLocalService, select ILocalServiceEscalation in the middle of the screen, and click OK.

4.   Select the OrderWorkflowLevel2Rejection event in the EventName property.

5.   Enter RejectionLevel2Invoked in the Invoked property, press Enter, and enter the following in its handler:

        MessageBox.Show("Level 2 approval for order: " + OrderNumber);

Configuring the Activities in the Timeout Branch

In the next step, you configure the activities in the Timeout branch of the Listen activity.

1.   Switch back to the workflow designer.

2.   Enter 00:02:00 in the Delay activity TimeoutDuration property.

3.   Double-click the Code activity and enter the following in its handler:

        MessageBox.Show("Level 2 timeout for order: " + OrderNumber);

Updating the Workflow Code-Beside File

In the two subsections of this section you add a dependency property and change the rules on the CheckCredit activity.

Add a DependencyProperty

In the next steps, you add a DependencyProperty named OrderAmount and initialize it when the workflow receives the order.

1.   Create a new DependencyProperty named OrderAmount by right-clicking in the code-beside file below the Status dependency property, selecting Insert Snippet, double-clicking Other, and then Workflow. Select the Dependency Property—Property choice, name it OrderAmount (leave the Property suffix), press Tab and set its type to double, and press Enter to leave the remaining defaults.

2.   Add the following code to the existing OrderInvoked handler below where the OrderNumber is initiated:

       OrderAmount = (e as SAMS24WFBook.OrderEventArgs).OrderAmount;

Change the Rules on the CheckCredit Activity

In the next steps, you will now check the order amount to determine approval in the IfElse activity rather than using the last digit of the order number. Orders less than $1,000 will be approved, orders larger than or equal to $1,000 and less than $2,000 require additional approval, and orders $2,000 or larger are rejected. You will create rules for the first two possibilities in the Approved and MoreInfo branches. There is no need for a third rule. It is the else.

1.   Switch to workflow designer view.

2.   Click the Approved branch of the IfElse activity, select Code Condition from its Condition property drop-down, click + to the left of the Condition property, and overwrite the current entry in the Condition property with OrderApprovedCondition.

3.   Enter the following in the handler:

        if (OrderAmount < 1000)
            e.Result = true;
        else
            e.Result = false;

4.   Switch back to the workflow designer.

5.   Click the MoreInfo branch of the IfElse activity, select Code Condition from its Condition property drop-down, click + to the left of the Condition property, and enter AdditionalApprovalCondition in the Condition property that appears after selecting Code Condition.

6.   Enter the following in the handler:

        if (OrderAmount < 2000)
            e.Result = true;
        else
            e.Result = false;

7.   Build the OrderEscalationWorkflows project and fix errors, if any.

Update the Forms (Hosts)

You update both the first level and escalation forms hosts in the subsections in this section.

Implement New Interface Members in the Escalation Form

Although the form is already created, the second-level approval and rejection events specified in the escalation interface must still be implemented. You will do so in the next steps.

1.   Open the EscalationForm in code view.

2.   Replace the existing class declaration with the following, which implements the escalation local service.

public partial class EscalationForm : Form,
SAMS24WFBook.ILocalServiceEscalation

3.   Add the following event declarations at the top of the local service implementation region (which look like this: #region Local Service Implementation):

        public event EventHandler<OrderLevel2EventArgs>
            OrderWorkflowLevel2Approval;

        public event EventHandler<OrderLevel2EventArgs>
            OrderWorkflowLevel2Rejection;

4.   In the EscalationForm designer, double-click the Approve button and add the following code to its hander:

        // Pass the Level2Approval event to the workflow
        OrderLevel2EventArgs eventArgs =
            new OrderLevel2EventArgs(workflowInstance.InstanceId,
"Approved");

        OrderWorkflowLevel2Approval(null, eventArgs);

5.   Double-click the Reject button and add the following code to its hander:

        // Pass the Level2Rejection event to the workflow
        OrderLevel2EventArgs eventArgs =
            new OrderLevel2EventArgs(workflowInstance.InstanceId,
"Rejected");

        OrderWorkflowLevel2Rejection(null, eventArgs);

6.   Build the EscalationForm project and fix errors, if any.

Implement the MoreInfo Method in the Basic Order Form

In the next steps, you implement the new additional approval request in the basic order form so that it can accept the additional approval request from the workflow. You do not need to modify the event arguments because no new events are added (or more specifically, no new event payloads are created).

1.   Add the following method to the BasicOrderForm.cs file below the Reject method in the Local Service implementation region:

        public void MoreInfo(string status, string orderNumber)
        {
            UpdateDelegate ud = delegate( )
            {
                textBoxStatus.Text = "Order No: " + orderNumber + " " +
status;

            };
            this.Invoke(ud);
        }

2.   Build the BasicOrdersForm project and fix errors, if any.

Configure Hosts to Track and Persist

In this section, you will configure both hosts to track and persist workflows. You will learn to configure multihost persistence, which requires setting the persistence ownership to work across hosts. Tracking is a new topic that is discussed next.

Add Tracking to Basic and Escalation Forms

Tracking is a mainstay feature in business process management and workflow packages that permits running workflows to emit information that can be used to examine the execution path taken by prior workflows and to see the execution path taken so far by currently processing workflows.

The emitted tracking data is generally saved to a storage medium. WF emits the standard workflow level events you have worked with (for example, as started, completed, loaded, unloaded, and idled). It also tracks the events of each activity on the workflow. Each activity contains the following possible events that are tracked: Canceling, Closed, Compensating, Executing, Faulting, and Initialized.

You will register the out-of-the-box SqlTrackingService in this hour, which will provide you with workflow and activity-level tracking. Then you will use the SqlTrackingQuery object and the WorkflowMonitor SDK sample to view the persisted tracking information. These tools show that simply registering the SqlTrackingService provides the capability to monitor workflow execution. Additional tracking features are covered in Hour 13, “Learning to Track Workflows.”

Add tracking to both workflow hosts. The BasicOrdersForm host takes the process from order receipt to completion if no second-level approval is required, and through waiting for second-level approval if it is required. The EscalationForm takes the process from waiting for second-level approval to completion.

In the next steps, you add tracking to both forms.

1.   Open the BasicOrderForm.cs file in code view and add the following using directive:

      using System.Workflow.Runtime.Tracking;

2.   Add the following code to the GetWorkflowRuntime method above the code that adds the persistence service to the runtime:

      // Add the sql tracking service to the runtime
      SqlTrackingService sts = new SqlTrackingService(connectionString);
      workflowRuntime.AddService(sts);

3.   Build the BasicOrdersForm project and fix errors, if any.

4.   Repeat steps 1 and 2 in the EscalationForm.cs file.

5.   Build the EscalationForm project and fix errors, if any.

That is all that is necessary to add tracking in WF. As you will soon see, this adds quite a bit of transparency and auditing capability to your workflows.

Add Persistence to Both Forms

In Hour 3, “Learning Basic Hosting,” you registered the SqlWorkflowPersistenceService with the host. While doing so, you set the SqlWorkflowPersistenceService parameters, which determined whether the workflow automatically unloaded when idle, specified the database connection string, specified how often the runtime should check for expired timers, and finally how long a workflow could remain locked. In Hour 3, you also set the value that determines locking to TimeSpan.MaxValue, which is the simplest thing to do. However, now that you are implementing more sophisticated persistence across forms, let’s look closer at what this property does and how it should be set.

When the WF runtime is processing a workflow, it locks the workflow record by setting a value. Processing means while the workflow is actually executing. This does not include the time a workflow is persisted. The lock is removed each time the workflow persists. Therefore, other WorkflowRuntimes can access the workflow because it is no longer being used by that host. This default behavior is fine except when a server crash or other problem occurs that causes a workflow to be abandoned by a host during processing. In this case, the workflow remains locked for the TimeSpan.MaxValue default period (approximately 10,000 years).

You can override the default workflow locking behavior by setting a different ownership duration. In case a workflow execution fails, the workflow will remain locked for this period of time and not 10,000 years (or more likely, until someone goes in and manually modifies the table).

You must understand one major caveat when overriding the workflow locking behavior. If you do not set it for a long enough time period, your workflow will fail. This is because when you go to persist again, there will be no lock on the record and the WorkflowRuntime will not update it. Therefore, you must set this value to a period that is longer than your longest workflow burst. This is generally a matter of seconds (or even less). To be safe, you might want to set it at a couple of minutes or some other value that would not be reached even during abnormally slow processing.

In the next steps, you modify the period of time that the workflow remains locked for two minutes.

1.   Open the BasicOrderForm.cs file and replace the current line of code that sets the ownershipDuration value with the following:

         TimeSpan ownershipDuration = new TimeSpan(0, 0, 2, 0);

2.   Just to be sure, build the BasicOrdersForm project and fix errors, if any.

3.   Open the EscalationForm.cs file and replace the current line of code that sets the ownershipDuration value with the following:

         TimeSpan ownershipDuration = new TimeSpan(0, 0, 2, 0);

4.   Just to be sure, build the EscalationForm project and fix errors, if any.

The workflow will be owned by whichever host is running it for two minutes between persistence points. This gives each plenty of time to process a burst and allows the workflow to be accessed again in case of a crash of some type.

Image

If you are using SQL Server 2000 and you store your tracking and persistence tables in the same database, you will spawn distributed transaction coordinator (DTC) by default, even though they are stored in the same database. The workaround for this is to use the SharedConnectionWorkflowCommitWorkBatchService. Using this service is not covered in this book. See MSDN or another source if this applies to you.

Retrieve Tracking Data

The SqlTrackingQuery class provides methods that return tracking data from the tracking database created by the SQL tracking service. We will return the tracked workflow and activity data. In this example, the retrieved information will be stored in a text box, but it could just as easily go on a report or be used for other purposes. The WorkflowMonitor SDK application demonstrated at the end of this hour shows another example of what can be done with the standard emitted tracking data.

In the next steps, you add code to query the tracking data.

1.   Open the EscalationForm and double-click the Get Tracking command button. Add the following code to its handler:

        ShowTracking(workflowInstanceID);

2.   Add the ShowTracking method below the Get Tracking command button handler as shown:

         void ShowTracking(Guid instanceId)
         {
         }

3.   Instantiate a StringBuilder and SqlTrackingQuery objects in the ShowTracking method as shown:

         StringBuilder sb = new StringBuilder( );

         // Create a new SqlTrackingQuery object.
         SqlTrackingQuery sqlTrackingQuery = new
            SqlTrackingQuery(connectionString);

         // Query the SqlTrackingQuery for a specific workflow instance ID.
         SqlTrackingWorkflowInstance sqlTrackingWorkflowInstance;

         sqlTrackingQuery.TryGetWorkflow(instanceId, out
            sqlTrackingWorkflowInstance);

4.   Check whether any records are returned by adding the following code (below the code you just added):

         // Check whether there is a matching workflow with this ID.
         if (sqlTrackingWorkflowInstance != null)
         {
         }

5.   Add code between the brackets (from the previous step) to iterate the workflow tracking records (workflow-level events) and populate the string builder added in the previous step, as shown:

         // Examine the workflow events.
         sb.AppendLine("Workflow Events:");

         foreach (WorkflowTrackingRecord workflowTrackingRecord in
            sqlTrackingWorkflowInstance.WorkflowEvents)
         {
            sb.AppendLine("EventDescription : " +
               workflowTrackingRecord.TrackingWorkflowEvent +

               " DateTime : " + workflowTrackingRecord.EventDateTime);
         }

         sb.AppendLine("");

6.   Add code below the code just added to iterate the activity tracking records (activity-level events) and populate the string builder as shown:

         // Examine the activity events.
         sb.AppendLine("Activity Events:");

         foreach (ActivityTrackingRecord activityTrackingRecord in
           sqlTrackingWorkflowInstance.ActivityEvents)
         {
            sb.AppendLine("Activity Qualified ID " +
               activityTrackingRecord.QualifiedName +
               "     StatusDescription : " +
               activityTrackingRecord.ExecutionStatus +
               "     DateTime : " + activityTrackingRecord.EventDateTime);
          }

7.   Add code below the code you just added to add the string builder text to the text box on the form as shown:

         textBooxTrackingInfo.Text = sb.ToString( );

8.   Build the EscalationForm project and fix errors, if any.

9.   The ShowTracking method should look like Listing 5.1.

LISTING 5.1 ShowTracking Method


        void ShowTracking(Guid instanceId)
        {
            StringBuilder sb = new StringBuilder( );

            // Create a new SqlTrackingQuery object.
            SqlTrackingQuery sqlTrackingQuery = new
               SqlTrackingQuery(connectionString);

            // Query the SqlTrackingQuery for a specific workflow instance ID.
            SqlTrackingWorkflowInstance sqlTrackingWorkflowInstance;

            sqlTrackingQuery.TryGetWorkflow(instanceId, out
            sqlTrackingWorkflowInstance);

            // Check whether there is a matching workflow with this ID.
            if (sqlTrackingWorkflowInstance != null)
            {
                // Examine the workflow events.
                sb.AppendLine("Workflow Events:");

                foreach (WorkflowTrackingRecord workflowTrackingRecord in

                  sqlTrackingWorkflowInstance.WorkflowEvents)
                {
                   sb.AppendLine("EventDescription : " +
                      workflowTrackingRecord.TrackingWorkflowEvent +
                      "  DateTime : " + workflowTrackingRecord.EventDateTime);
                }

                sb.AppendLine("");
                // Examine the activity events.
                sb.AppendLine("Activity Events:");

                foreach (ActivityTrackingRecord activityTrackingRecord in
                  sqlTrackingWorkflowInstance.ActivityEvents)
                {
                    sb.AppendLine("Activity Qualified ID " +
                       activityTrackingRecord.QualifiedName +
                       "     StatusDescription : " +
                       activityTrackingRecord.ExecutionStatus
                       "     DateTime : " +
activityTrackingRecord.EventDateTime);
                }
                textBooxTrackingInfo.Text = sb.ToString( );
            }
        }


Image

The SqlTrackingService is the only OOB tracking service shipped with WF. You can, however, create your own custom tracking services based on the TrackingService base class to store tracking information to a file, an Oracle database, or any other medium you want to. Doing so is not covered in this book. There are ConsoleTrackingService and FileTrackingService samples in the WF, WCF, and CardSpace samples. Details for accessing these samples can be found in Hour 1, “Understanding Windows Workflow Foundation.”

Retrieve the Workflow from Persistence

The escalation form needs to be able to retrieve workflows that require second-level approval. It will do so by extracting the workflow from the persistence store. You will use the SqlWorkflowPersistenceService.GetAllWorkflows method to retrieve all persisted workflows and add them to a combo box. This method returns a collection of all persisted workflows that can be iterated through (this is described in slightly more detail at the end of this section). The selected workflow will then be loaded into memory, where it can be approved, rejected, and tracking information can be retrieved.

In the next steps, you retrieve the workflow saved to the persistence store in the first level from in the escalation form.

1.   Add the following method to the top of the persistence extraction region in the EscalationForm.cs file.

        private void GetPersistedWorkflows( )
        {
            comboBoxOrders.Items.Clear( );
            foreach (SqlPersistenceWorkflowInstanceDescription
                sqlWid in sqlPersistenceService.GetAllWorkflows( ))
            {

comboBoxOrders.Items.Add(sqlWid.WorkflowInstanceId.ToString( ));
            }
            comboBoxOrders.Refresh( );
        }

2.   Double-click the ComboBox on the form and add the following code to its handler to load the selected workflow back into memory:

            if (comboBoxOrders.Items.Count != 0)
            {
                workflowInstanceID = new Guid(comboBoxOrders.Text);

                // Retrieve the selected workflow from the persistence
service
                // and load it.
                workflowInstance =
workflowRuntime.GetWorkflow(workflowInstanceID);
                workflowInstance.Load( );
            }

3.   Add the following code to load the workflows into the combo box at the end of the EscalationForm_Load method:

       // Get the persisted workflows from the sql persistence service
       GetPersistedWorkflows( );

4.   Add the following code to load the workflows to the end of the workflowRuntime_WorkflowCompleted event handler so that the completed workflow is removed from the combo box:

            UpdateDelegate ud = delegate( )
            {
                GetPersistedWorkflows( );
            };
            this.Invoke(ud);

      At this point, the workflow is loaded back into memory and ready to receive second-level approval response. There are a couple of new items introduced:

Image   First, the workflows are retrieved from the persistence store by calling the SqlWorkflowPersistenceService.GetAllWorkflows method. The SqlPersistenceWorkflowInstanceDescription type is returned for each workflow returned from the store. This type contains the WorkflowInstanceId, information on the next timer expiration, whether the workflow is blocked, and other information.

Image   The workflow retrieved from the combo box is loaded back into memory by calling the WorkflowRuntime.GetWorkflow method, and then calling the WorkflowInstance.Load method. This has the same effect as calling the WorkflowRuntime.CreateWorkflow method and then calling WorklfowInstance.Start method on a new workflow instance.

5.   Build the EscalationForm project and fix errors, if any.

Running the Solution

You will run three workflows. In the first two, you examine the different persistence behaviors in workflows that do and do not require second-level approval. The third looks at tracking and shows the workflow execution using the tracking. All the workflows that contain second-level approval demonstrate the Listen activity and escalation support added to the workflow.

First-Level Approval or Rejection

Enter a workflow with a value less than 1,000 or larger than 2,000 and check the persistence table. You will see that the workflow did not persist because additional approval was not required.

In the next steps you run the workflow using a value that will not persist because it does not require additional approval. You will then check the persistence database to validate whether or not the workflow persisted.

1.   Set the BasicOrdersForm project as the startup project and run the solution.

2.   Enter any order number and an order amount with a value less than 1,000 and click Submit.

3.   Open the WFTrackingAndPersistence database (in SQL Server Management Studio or through other means), select the InstanceState table, and choose to show all rows. The contents should be empty because nothing should have been persisted. The workflow didn’t require second-level approval and therefore is not persisted while waiting for approval.

4.   Exit the basic order form.

Second-Level Approval or Rejection

In the next steps, you will enter an amount that triggers persistence and verify this by checking the database. Then you will use the escalation form to provide second level approval.

1.   Run the solution and enter any order number, an order amount with a value between 1,000 and 2,000 (noninclusive), and click Submit. The order form should look like Figure 5.4 (the text box should contain the message second level approval required).

FIGURE 5.4 Order that requires additional approval.

Order that requires additional approval.

2.   Now refresh the InstanceState table, and you should notice an entry in the table that looks similar to Figure 5.5:

FIGURE 5.5 Workflow persisted in InstanceState table.

Workflow persisted in InstanceState table.

3.   Exit the basic order form.

4.   Set the EscalationForm project as the startup project.

5.   Load the escalation form (shown in Figure 5.6), select the workflow from the combo box, click the Approve button, and click OK when the workflow is approved and completed dialogs appear.

FIGURE 5.6 Workflow loaded in escalation form to be approved or rejected.

Workflow loaded in escalation form to be approved or rejected.

6.   Refresh the InstanceState table; there should no longer be any records because the workflow is completed.

Second Level Approval and Tracking

The escalation form contains a Get Tracking button and a text box to store the tracking information. In this run of the workflow, you will view the tracking results before and after processing second-level approval. Previously, activity execution will have stopped at the WaitForResponse Listen activity. After second-level approval, the workflow executes the remaining activities. The remaining activities will take it down one of the Listen activity branches (for example, Approved, Rejected, MoreInfo).

In the next steps you will enter an amount that requires second level approval again. This time, you will utilize tracking to monitor the process.

1.   Set the BasicOrdersForm project as the startup project and run the solution.

2.   Enter any order number, and an order amount with a value between 1,000 and 2,000 (noninclusive), and click Submit.

3.   Exit the basic order form.

4.   Set the EscalationForm project as the startup project and load the escalation form.

5.   Select the workflow from the combo box.

6.   Click the Get Tracking button. The text box at the bottom of the form should contain the workflow and activity execution. As mentioned, the final activity event will be WaitForResponse, as shown in Figure 5.7.

FIGURE 5.7 Workflow escalation form with tracking run through second-level approval.

Workflow escalation form with tracking run through second-level approval.

7.   Click the Approved button (and click OK on dialog buttons) and then click the Get Tracking button again. Your form should look like Figure 5.8. You will have to scroll to see all the activity events.

FIGURE 5.8 Workflow escalation form with tracking run through completion.

Workflow escalation form with tracking run through completion.

Tracking information, unlike persistence, is retained after workflow completion. Therefore, tracking provides a repository of runtime information. By default activity-and workflow-level events are tracked. Custom information, such as order number and amount, can also be tracked. The means to capture custom tracking information will be covered in Hour 13, “Learning to Track Workflows.”

Capturing the execution history of workflow- and activity-level events, however, is quite powerful in itself, as you will see next.

Running the WorkflowMonitor SDK Sample

So far, you have tracked information and used queries to place the results in a text box. The WorkflowMonitor SDK sample extracts the information from running workflows and displays it in a graphical format similar to the way workflows are displayed in the workflow designer. It achieves this by rehosting the workflow designer (see the next note). Many workflow/business process management systems feature sophisticated process-monitoring capabilities that leverage business intelligence and other tools to make process data available for analysis and alerts. WF has the capability to extract the information from running processes, and the SDK sample shown in this section offers basic monitoring capabilities. These capabilities are significant though, because they alone provide the ability to monitor workflow state and to project its future execution paths. You can customize the sample because it comes with source code.

Image

Rehosting the workflow designer is not covered in this book. See Hour 1, “Understanding Windows Workflow Foundation,” for designer rehosting resources if you are interested.

Downloading and Installing WorkflowMonitor

In the next steps, you download and install the WorkflowMonitor SDK sample.

1.   Copy both the BasicLocalService.dll and BasicLocalServiceWorkflow.dll from C:SamsWf24hrsHourHour05EscalationWorkflowLab1OrderEscalationSolutionBasicLocalServiceWorkflowinDebug to your sample installation directory WFApplicationsWorkflowMonitorCS WorkflowMonitorinDebug.

2.   If you have not already downloaded the WF sample applications, go to http://msdn2.microsoft.com/en-us/library/ms741706.aspx and download and install them in any directory. My directory is C:WFSamplesWCF_WF_CardSpace_Orcas_Beta2_SamplesWF_SamplesSamplesApplicationsWorkflowMonitorCSWorkflowMonitorinDebug, which is a manifest of following the default installation. I will assume this directory; substitute as appropriate if you use an alternative directory.

Image

You must also copy BasicLocalService.dll and BasicLocalServiceWorkflow.dll from C:SamsBookHourHour05EscalationWorkflowLab1OrderEscalationSolutionBasicLocalServiceWorkflowinDebug to C:WFSamplesWCF_WF_CardSpace_Orcas_Beta2_SamplesWF_SamplesSamplesApplicationsWorkflowMonitorCSWorkflowMonitorinDebug. Yes, you do need the local service DLL as well. Alternatively, you could copy your workflow assemblies to the GAC instead of to the workflow monitor debug directory.

3.   Go to the WorkflowMonitor directory and start the WorkflowMonitor solution.

4.   You may receive an error because no tracking database has been configured with the application. If so, ignore it because you will configure it now.

5.   If the Settings form does not automatically pop up, click the Monitor menu choice at the top of the form and select its Options choice. You should see the form shown in Figure 5.9.

FIGURE 5.9 Workflow escalation form with tracking run through completion.

Workflow escalation form with tracking run through completion.

6.   Enter your server name and tracking database name and click OK.

7.   You will now see the escalation workflows and the WorkflowMonitor application, as shown in Figure 5.10.

FIGURE 5.10 Workflow Monitor tool in action.

Workflow Monitor tool in action.

8.   You can move from workflow to workflow by clicking different workflows in the upper-left pane.

9.   If you want to filter only to running workflows, you can select Running from the Workflow Status drop-down and click the magnifying glass to the left of Workflow Instance ID.

The middle pane contains the workflow designer. Some activities are check marked. This signifies that they were executed. Therefore, you can observe the execution path of previously executed workflows and also the “path-to-date” for currently running workflows.

The upper-left pane lists the workflows. The lower-left pane shows the activities that have executed for the currently selected workflow. A navigation bar across the top permits the workflows displayed to be filtered by various states (such as all running workflows). Workflows can also be filtered based on workflow and activity properties, such as order amount.

In a real-world scenario, you would need to filter by values that are less than or greater than, as well as equal to, and there would be a need for and/or operators to form useful queries (such as orders over $1,000 that are on credit hold). Although this level of functionality is not offered OOB, the source code is included, and you can add this type of functionality if you need it.

Tracking is WF’s main vehicle for adding runtime transparency to WF that can be combined with WF’s inherent design-time transparency. The combination of design-time and runtime transparency is one of the WF’s main features. WF has to be embeddable and appropriate to all types of Windows applications. Through adding the persistence and tracking services, you are seeing how it is able to mold itself to the needs of the host and fulfill its appropriate quota.

Summary

This hour saw the creation of a workflow application, demonstrated the use of tracking, extracted workflows from the persistence service, and explored the Listen activity, a seminal workflow artifact.

It is very common that people want to know why they should use WF. You have already seen the following areas where WF offers benefits:

Image   Runtime transparency through tracking (and designer rehosting).

Image   The ability to wait for events and to time out if no event arrives to support escalation scenarios (also known as asynchronous process support).

Image   Inherent design-time transparency through its modeling capabilities.

You will learn of many new WF features, such as Custom activities. You will also learn to enhance the escalation capabilities learned in this hour throughout the remainder of this book. However, the combination of runtime transparency, asynchronous process support, and inherent design-time transparency are solid benefits in and of themselves. You will learn about state machine workflows over the next two hours.

Workshop

Quiz

1.

What constraint does an EventDriven activity impose?

2.

What activity composes a branch of a Listen activity?

3.

What method is used to retrieve workflows from the SqlWorkflowPersistenceService?

4.

Where do your workflow assemblies need to reside for the WorkflowMonitor service to use them?

5.

What do you use the SqlTrackingQuery type for?

6.

When are persisted workflows removed from the persistence store?

7.

What benefit do you get by registering the SqlTrackingService?

Answers

1.

Its first child must be a blocking activity such as a Delay or HandleExternalEvent activity.

2.

EventDriven.

3.

SqlWorkflowPersistenceService.GetAllWorkflows.

4.

The directory that contains the WorkflowMonitor DLL or the GAC.

5.

To query workflows saved by the SqlTrackingService.

6.

When the workflow completes (successfully or unsuccessfully).

7.

All workflow and activity events are tracked. Through this you gain runtime transparency, which, for example, the WorkflowMonitor exploits.

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

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