Implementing Manual Activities in Windows Workflow
Windows workflow is an excellent framework for implementing business processes. One thing that is missing in it is direct support for human activities. Although several approaches to solving this problem appear in Microsoft publication [1, 2], they are not generic enough for general usage. In this article we will define one of the approaches to a completely generic implementation of human activities in WF.
The complexities of supporting human interactions present multiple challenges. Some of them are:
- Duration of the user respond (execution time of user activity) is unpredictable.
- User can be not connected to the system, when request occurs and consequently a request needs to be stored and presented to user when he logs in.
- There can be multiple workflows running simultaneously, hosted by different machines, but a user typically wants a consolidated view of all of his tasks.
These issues have been realized by the industry and resulted in two major specifications aimed to solve these problems [3, 4]. We will try to build our implementation following the spirit, not the letter of the specifications.
Components view of a solution
Main components of the overall solution are presented at Figure 1.
Figure 1 Solutions components
In the heart of the solution is a work queue manager. This is a centralized service keeping track of all tasks for all of the users of the system. Any workflow (or a service/application containing workflow), which requires a human activity, invokes a custom workflow activity , which submits the request to the work queue manager persisting them and allowing other components of the system to work with these requests. By doing this a work queue manager is becoming a decoupling layer between workflow engine and human activity execution, thus providing support for the cases where user is not present in the system during workflow execution. Also by being a centralized service a work queue manager allows to combine all of the tasks for a given user, regardless of the process that they have initiated from. Because different user tasks can require different input information and can produce different outputs, communications between workflow and human activity is XML in and XML out, which allows processing any possible request and response generically. Although usage of XML might seem as a complicating factor for the implementation, an excellent support for XML serialization, implemented in .Net  makes mapping between XML and objects a breeze. A work queue viewer is a GUI application allowing a user to see all of the tasks that are ready for his input. This application is generic, displaying only essential elements of a task, including, name, type, priority, creation, etc. Based on this information in the queue a user can decide to work on a particular task. The actual processing of the task is done by a task application, supporting functionality, specific for a given task. A Work Queue maintenance application provides a UI for administrative support of Work Queue manager. It allows viewing and modifying existing human task, viewing their history, etc. Finally a Human activity is a custom activity (see sidebar "Windows Workflow Foundation Component Model") implementing communications with the Task queue manager and presenting a very simple programming model for the human task execution to the workflow developer. From his point of view human interaction becomes no different than an ordinary service invocation.
Windows Workflow Foundation Component Model
As described in , Windows Workflow Foundation (WWF) implementation is very different from executable workflow languages (domain languages) dominating today in workflow implementations. In WWF "activities in a process graph are linked to a component that implements the runtime behavior of that activity in a general purpose programming language. Each activity type in a process language corresponds to one implementation component. For example a web service invocation activity, a human task activity or an email activity all correspond to an implementation component."
As a result, it is extremely simple to extend WWF through introduction of new activity types, implementing runtime behaviors, required in a particular case (human task activity in our case). This approach makes it fairly simple to build new or extend existing process languages through implementations of new activities.
The overall interaction of components is presented at sequence diagram (Figure 2)
Figure 2 Sequence Diagram
As we can see from the above the overall solution includes two types of components:
- Generic, including human activity, work queue manager and work queue viewer. These components operate on "standard" attributes of a human task and treat task's input and output as generic XML.
- Specific, including workflow itself and task processing business applications, which implement XML serialization/deserialization into specific business objects and use those objects for their functioning.
In this article we will describe only generic components of solution.
Work Queue Manager
Service interfaces and functionality
As we have defined above, a work queue manager is a centralized service. Its functionality is based on the data contracts, presented at Figure 3.
Figure 3 Work Queue Manager Data Contracts
The major components of this data model are:
- Human task - this data type defines the major elements of a human task (following ), including:
- TaskID - a unique identifier for a task
- Task Name - a human readable identifier for a task (non unique)
- Task status - a state of the task. Possible states of the task are defined by TaskStatusEnum data element and can be: Ready, Reserved and completed. A simplified Task state transition diagram is presented at Figure 4.
- Task priority can be assigned to a task during its creation and used to signify task importance. Work Queue Viewer presents the tasks, based on their priority, with the tasks of the hire priority put on top of the queue.
- CreatedOn and createdBy attributes, specify task's creation timestamp and user which has created a task. They can be used to decide on processing order of tasks in the queue.
- Task type is a type of a task used for choosing an appropriate business application for processing a task. Currently we are choosing a combination of workflow/task name (Task type data element). By separating a task type in a corresponding data type we allow for simplification of switching to another way of defining of a task type definition (if required).
- ReservedBy, ReservedOn are used for locking and ensuring that only one user at a time is allowed to work on a particular task.
- CompletedBy and CompletedOn elements do not impact task processing and are there more for the book keeping services for maintenance application and/or reporting
- Potential Owners allows specification of a list of users, which are allowed to view/work on this task. Currently a potential user can be defined either as a specific user or a user's group (User data type)
- Escalations allow identification of a list of escalations, which can apply to a task. Every escalation (escalation data type), can be defined as either notification (notification data type) or reassignment (reassignment data type). Both types of escalation contain a timeout after which an escalation applies (the timeout is from task creation) and email (for notifications) and a new list of potential users (for reassignments). Current implementation does not support escalations.
- Escalated flag identifies whether a task has been escalated.
- Task request and reply fields contain stringified versions of task's request and reply.
- CallbackInfo is a data type that is used by a human activity to specify information required by a work queue manager to send a reply back to the human activity, signaling completion of the task execution. This type contains category, name and version of the service along with consumer type of the work queue manager process . The implementation relies on a service registry to resolve this information into the actual endpoint address, binding and binding parameters.
- WorkflowParameters data type is a data type that is used to route the reply back to the appropriate place of the appropriate workflow instance. It includes workflow ID (identifying workflow instance) and a correlation (identifying workflow step/activity)(Because custom Human activity implementation is based on workflow queues, we are currently using workflow queue name as a correlation).
- HumanTaskView is a data type, specifying a subset of human task data type, and is used to return information about tasks to a task queue viewer application.
- WorkItemQuery data type is used by task queue viewer application to request information about current tasks.
- MaintenanceQuery data type is used by maintenance application to specify a query for viewing human tasks.
Figure 4 Task state transition diagramWhen task is submitted to the work queue manager, it is in a ready state. Once it is requested by an application for an execution it transitions to a ready state. When an application completes task process, task moves to a completed state, which signals work queue manager to return response to a human activity about task completion. Alternatively if task processing returns cancel or times out, a task returns back to a ready state.
In addition to the data types, data model defines Fault data types, presented at Figure 5.
Figure 5 Fault Data types
A work queue manager exposes three groups of services - Workflow services, User services and Maintenance services.
Workflow service section (Figure 6) defines a service, which is used by a human task activity to submit a task for an execution, and an interface which has to be exposed by a workflow service and is used by a work queue manager to signal execution completion. These two services provide integration between human activity, running in a workflow and a work queue manager.
Figure 6 Workflow Services
Submit for execution service, implemented by a work queue manager, receives requests from human activities that can be part of many different workflows. Once the request is received, it is stored in a database for a further processing.
A completeExecution contract, although defined here, is implemented by a workflow. This service is used to signal workflow that an execution of a particular task is completed and a workflow can proceed.
User services (Figure 7) are implemented in support of user's interactions with a work queue manager. WorkWithItems service supports three operations:
- GetItemsList method is used by a work queue viewer and returns a list of 'Ready' tasks (optionally of a given type) for a given user.
- WorkWithItem method is used by a business application to get complete information about the task, which it wants to work with. At this point user credentials are compared with the list of potential owners for this task and notAuthorized exception is returned if a user is not a part of this group. This request additionally triggers a status change of the task - once business application starts to work with a task, its status is changed to Reserved and reservedOn and reservedBy fields are filled with the appropriate information. This simple reservation (locking) mechanism ensures, that only one user is working with a task at a given time. If a business application is trying to access reserved task, the fault is returned by an operation, indicating that a task is processed by another user (fault provides information about user and a time he/she started working with a task). In order to avoid an infinite task locking, the service is expiring lock. If a request is received to list a task or work with Item for a tasked that was in Reserved state for more than 30 minutes, this task's state is changed to Ready.
- CompleteExecution method is used by a business application to signal to the work queue manager that task processing is completed. A business application returns completion status, which can be either completed or aborted. If the returned status is completed, then a task execution status is changed to Completed and a service is invoked to signal a human activity task completion. If the returned status is aborted, then the task status is changed to Ready.
Figure 7 User Services
Maintenance services (Figure 8) are implemented in support of work queue manager maintenance application.
Figure 8 Maintenance Services
View tasks method returns a list of existing task using a filter defined by MaintenanceQuery data type. After looking at the tasks, they can be modified and changes can be returned to the work queue manager using Update task method.
Underlying database and data access
Service database design (Figure 9) Contains tables, necessary for persisting all of the information associated with the human task. The main table - Human task table contains all the basic human task information, including task name, ID (assigned by a service), priority and task life cycle events, supplemented by information about who and when have executed these events.
Figure 9 Work Queue Manger Database
Additional tables contain additional task information and include:
- Callback and TaskCallback tables. Callback table contains information about service, implemented by a workflow process and listening on requests from a human task manager. Design relies on usage of the registry and thus is using service name and version that is sufficient for resolving service endpoint address and binding information. Multiple human tasks can use the same callback service that is why we have implemented it as a separate table. TaskCallback table is a junction table, linking HumanTask and callback tables.
- TaskType and TaskTaskType tables. TaskType table contains information about task's type. Because we can have many human tasks of the same type, we separated this information in its own table. TaskTaskType table is a junction table between HumanTask and TaskType tables.
- WorkflowReference table contains information, allowing linking a human task to a particular instance/activity of a workflow. It contains workflow instance ID and the name of the workflow queue (see below), that was used by an instance. Because this mechanism can change in future, we have separated this information into its own table. Relationship between human task and workflow reference is always one-to-one, consequently instead of using junction in this case, we are linking WorkflowReference and HumanTask tables using foreign key.
- PotentialUser and TaskPotentialUser. Every task can have a list of potential users and every user in the system can have a list of tasks, that he is authorized to work with. Consequently we are using PotentialUser information to keep track of users and TaskPotentialUser junction table to link users to tasks.
- Notification, Reassignment, TaskNotification, TaskReassignment and ReassignmentPotentialUser tables. These tables will support escalations, when escalations will be implemented.
In order to optimize data access we have also implemented several stored procedures and a view. Stored procedures are:
AddTaskCallbackis a stored procedure for adding callback information and associating it with a given human task (using
InsertTaskCallbackstored procedure, below). It first checks whether a given callback already exists and if it does, uses existing callback record, otherwise creating a new one in
Callbacktable. It then invokes an
InsertTaskCallbackstored procedure to create a link.
InsertTaskCallbackis a stored procedure populating
TaskCallbacktable, thus creating a relationship between a given callback and a human task.
AddTaskTypeis a stored procedure for adding task type information and associating it with a given human task (using
InsertTaskTypeTaskstored procedure, below). It first checks whether a given task type already exists and if it does, uses existing task type record, otherwise creating a new one in
TaskTypetable. It then invokes an
InsertTaskTypeTaskstored procedure to create a link.
InsertTaskTypeTaskis a stored procedure populating TaskTaskType table, thus creating a relationship between a given task type and a human task.
AddTaskPotentialOwneris a stored procedure for adding potential owner information and associating it with a given human task (using
InsertTaskPotentialUserstored procedure, below). It first checks whether a given potential owner already exists and if it does, uses existing potential owner record, otherwise creating a new one in
PotentialUsertable. It then invokes an
InsertTaskPotentialUserstored procedure to create a link.
InsertTaskPotentialUseris a stored procedure populating
TaskPotentialUsertable, thus creating a relationship between a given potential owner and a human task.
Usage of these stored procedures decreases the amount of database access by about a factor of 2 while minimizing the amount of database data by eliminating duplicates.
Database view (Figure 10) simplifies database accesses for reads. All of the task information (including task type) for a given user can be obtained from a single view.
Figure 10 Complete task view
A class diagram for the persistence layer, implementing repository pattern, is presented at Figure 11.
Figure 11 Persistence class Diagram
The overall implementation of the service is fairly straight forward. Once a particular method is invoked it delegates execution to the repository class (Figure 11), which implements all required database access.
A human activity is a custom workflow activity implementing human interaction. This activity hides communications with the work queue manager from a workflow designer and allows treating user interaction similar to an ordinary service invocation.
Implementation of this activity is based on workflow queues  providing asynchronous communication between activities and the outside world: activities register to receive messages on a queue and services send messages on queues. A custom activity can use this model both to handle external events as well as to communicate completion of asynchronous activity execution. This allows an activity to execute up to a point, and then wait for stimulus in order to continue execution. The overall interaction of such implementation is presented at Figure 12.
Figure 12 Activity implementation using Workflow Queue
Once started, activity execution will continue for as long as it can. Upon reaching activity point that requires external execution, the activity registers with a workflow queue. Then the workflow instance enters the waiting state and can be passivated (persisted). Once an external execution completes, it enqueues information into the queue. At this point a runtime reactivates a workflow instance, which continues execution.
The actual implementation of human activity consists of two parts - activity itself and a callback service.
A Human activity sends a message to the human task manager to start a new human task processing. If invocation of this service is successful, the activity registers itself to a workflow queue and goes into waiting state. If there are any errors, executing service, an activity throws a workflow fault that can be processed by a workflow.
When a reply is received by a service it enqueues the reply to the activity's work queue. A queue message wakes up an activity, which dequeues reply and finishes its execution.
A callback service implements complete execution contract (Figure 6) and has to be started by a service/application implementing workflow. In order for this service to work correctly, a
WorkflowRuntime has to be set on it. This can be done using a public static variable on a
A human activity needs several parameters for its execution - task type, priority, etc. These parameters are defined as
DependencyProperty can then be exposed to be set by workflow designer , thus allowing setting activity parameters visually with no programming (Figure 13).
Figure 13 Configuring Activity Parameters
Work Queue Viewer
A work queue viewer is implemented as a user control (Figure 14), which can be put on any form. This control implements a dataview control and takes a delegate as a parameter. When any cell of a given row is clicked, a delegate is invoked with a task ID and task type. Figure 14 shows a simple delegate that just pops up a message box.
Figure 14 Work Queue Viewer
Despite the push for complete automation of the business processes, human activities still play, and will continue to play an important role in business process implementations. As we have defined in the beginning of the article introduction of user interactions brings a lot of additional concerns, that are not directly related to workflow implementation. Completely decoupled implementation, described in this article, allows for separation of concerns between workflow development and user interaction development. Additionally, centralization of the human tasks support in the human task manager simplifies an aggregation of task management for a given user.
Many thanks to Paul Rovkah and Rob Sheldon for their help in putting this article together.
1 Jeremy Boyd. Integrating Windows Workflow Foundation and Windows Communication Foundation. MSDN January 2007.
2 Windows Workflow Foundation Web Workflow Approvals Starter Kit?. Microsoft Downloads.
3 Web Services Human Task (WS_HumanTask).
4 WS BPEL extensions for People.
5 Matt Milner. Build Custom Activities To Extend The Reach Of Your Workflows MSDN Magazine, December 2006,
6 Dare Obasanjo XML Serialization in the .NET Framework. January 2003,
7 Serge Luca. Using the Windows Workflow Foundation Queuing system.
8 Glenn Block Attached Properties and the Workflow Designer.
9 Dennis Pilarinos. Getting DependencyProperty RegisterAttached properties to appear in the Property Browser redux.
10 B.Lublinsky, Implementing a Service Registry for .NET Web Services. January 2008, InfoQ,
11 Tom Baeyens. Process Component Models: The Next Generation In Workflow? February 2008, InfoQ.
We are working on a project involving workflows and lots of human activities, and this article re-enforces what we started to put in place.
Is there any plan for publishing the source code?
thank you for this excellent article. I really enjoyed it.
Like julien I would be very interested in the source code as well. Could we have some feedback if a release of the source is possible or if we have to implement it ourself.
It's the Humans Stupid!
It will be helpful to see the source code for this if possible.
Would like to now more..