It
is generally possible to describe a business process as a workflow
comprised of a discrete series of steps that involves people and
software. WF is a Windows platform specifically for workflow technology
(Figure 1).
At its core, WF enables the execution of steps required to complete a
business process. It is used to construct workflow-enabled services and
service compositions for which it provides the following:
an extensible programming model
a workflow engine
design tools for Visual Studio
a mechanism to invoke services and to publish workflows as services
WF technology is
applicable to document-centric workflows, human workflows, business
rules-driven workflows, and other variations. It is used to enable both
human and automated workflow steps and to wire up opaque blocks of
functionality called activities.
WF Architecture
The major parts of the WF platform (Figure 1)
include workflow, activities, the WF base activity library, the WF
runtime engine, and WF runtime services. Each part is further explained
in Figure 2 and Table 1.
Table 1. Descriptions of the major parts of WF.
Part | Description |
---|
activity | a unit of work or a discrete step in a business process |
workflow | a sequence of activities |
WF runtime engine | a
workflow instance is created and executed by the WF runtime engine
(which also manages state and communication with the host process) |
WF designers | UI tools used to implement a workflow using shapes (Visual Studio includes a WF designer) |
WF runtime services | services that provide hosting flexibility and communication |
host process | an
application that hosts the WF runtime engine which executes the
workflow (the host process provides support for runtime services, such
as persisting the workflow’s state) |
WF runtime services are
connection points for plug-in resource providers. For example, the
default persistence behavior provided by the runtime engine can be
changed by providing a runtime service. WF further supports
compensating transactions for a given runtime service activity. Within
the solution logic, WF can define actual compensators that are invoked when exceptions occur.
Workflows
A strategic benefit to using
WF is that it allows for the use of a common workflow technology to
build workflow solutions across other Microsoft products and .NET
solution environments.
WF supports both system workflows and human workflows by supporting two built-in workflow types:
sequential workflows
state machine workflows
Both rely on the same
runtime environment and the same set of standard WF activities. A
single workflow definition can also be a composite of both types,
containing activities that rely on the system and on human action.
Let’s briefly explore each workflow type a bit more.
Sequential Workflows
Sequential
workflows execute activities in a pre-defined pattern. This workflow
type is better suited for system workflows because the execution of
activities closely resembles a flowchart with branches, decision logic,
loops, and other control structures.
By using WF, rather
than embedding workflow routines into the core logic of individual
services, each step in the business process is defined explicitly in a
graphic designer and executed by the workflow engine. This corresponds
directly to Process Centralization together with additional patterns that co-exist to establish an environment as per the Orchestration compound pattern.
The resulting level of
separation can cleanly partition agnostic and non-agnostic logic
allowing for the definition of reusable services that are independently
maintainable. This is essentially the basis of Functional Decomposition , which is commonly further supplemented with the sequential application of Service Encapsulation , Agnostic Context , Non-Agnostic Context , and Agnostic Capability .
These foundational service patterns can be applied to form primitive
service modeling and design processes that are initiated with
well-defined workflow logic and carried out using WF tools, such as
Workflow Designer (explained shortly).
Applying these patterns generally leads to the need to further define the functional contexts of services via Service Layers , such as those established by the application of the Utility Abstraction , Entity Abstraction , and Process Abstraction .
Agnostic utility and entity services that result from the application
of the former two patterns need to be separated within or outside of WF
in order to avoid unnecessary performance overhead and synchronization
issues due to deployment-level dependencies on the orchestrated task
service logic that encapsulates parent workflow routines.
|
State Machine Workflows
State machine workflows
execute activities as external events occur. This type (based on the
well-known Finite State Machine) is better suited for workflows
involving human intervention. The subsequent activity to be executed
depends on the current state and the event it has received. State
machine workflows are useful when the sequence of events is not known
in advance or when the number of possibilities makes defining all
possible paths impractical.
Workflow Designer
An advantage of creating
applications using workflows is the ability to define the workflow
graphically, which is why WF includes a designer for Visual Studio. By
default, the activities appear in the toolbox, letting a developer drag
and drop them onto the tool’s design surface to create a workflow. The
workflow designer is a convenient way to interact with the workflow
namespace.
Workflows
created with the designer are stored in a file based on XAML, a
declarative XML language used to define objects, their properties,
relationships, and interactions. Upon execution, the runtime engine
takes the XAML workflow and creates workflow instances. While there is
only one XAML-based workflow file, there can be multiple workflow
instances running at any given time.
The designer allows you to
model a Sequential Workflow Console Application or a State Machine
Workflow Console Application. After selecting the appropriate model you
need to add activities by dragging and dropping them from the toolbar.
(Note that the console application contains a sub main method that is
used to start the workflow.)
Workflow Persistence (with WF)
Workflows can be
dehydrated from memory and can later be re-hydrated and re-activated.
WF supports dehydration of the instance state by allowing a workflow
instance to be serialized to a data store, such as SQL Server. The
workflow instance can be restored to the original execution state at
any time by de-serializing the data based on events or messages.
The SqlWorkflowPersistenceService
class in WF is designed to connect workflows with SQL Server. In order
to use this persistence service, we need to create a database in SQL
Server with the schema that the persistence service uses. WF comes with
SQL scripts to create the database and schema used by the persistence
service.
The database and schema scripts are typically placed in the following folder after installing WF:
...\Microsoft.NET\Framework\v3.0\Windows Workflow Foundation\SQL
The scripts provided by WF are SqlPersistenceService_Schema.sql and SqlPersistenceService_Logic.sql. The former defines the structure of the database and the latter defines the stored procedures.
The following example provides code that creates an instance of SqlWorkflowPersistenceService
and an instance of the workflow runtime. The workflow runtime is used
to generate an instance of Workflow1 by dehydrating it from the
persistence database. The runtime starts the instance and the instance
ID is stored in the ID property for later use. Finally, the workflow
runtime stops and the state is de-serialized back to the database.
Example 1.
void Load() { WorkflowRuntime workflowRuntime = new WorkflowRuntime(); SqlWorkflowPersistenceService sqlPersistenceService = new SqlWorkflowPersistenceService(this.connectionString); workflowRuntime.AddService(sqlPersistenceService); workflowRuntime.StartRuntime(); WorkflowInstance instance = workflowRuntime.CreateWorkflow(typeof (WFWorkflow.CalcWorkflow)); this.id = instance.InstanceId; instance.Load(); instance.Start(); workflowRuntime.StopRuntime(); }
|
WF
includes a tracking service that allows a developer to save information
about a workflow’s execution to the database. For example, the start
date, time and end date, time of a workflow and its activities can be
saved to the database.
Communicating with the Host Container
The base WF library includes the CallExternalMethod and HandleExternalEvent activities used for communication with the host based on the request-response message exchange pattern.
Persisting workflow logic via a database is a classic application of State Repository as a result of the application of the Service Statelessness principle to the task or controller service that resides within WF runtime environment.
|
The WF has a built-in CallExternalMethod
activity, which can raise an event to be consumed by the host. The host
application needs to implement an event handler for receiving response
arguments from the workflow using a standard event-or-delegate pattern. The activity can also be used to send data from the workflow to the host. The HandleExternalEvent is used by the workflow instance to capture an event raised by the host application.
It is critical for the
interface outside the runtime execution context to communicate with the
code in the host application process. The barrier is bridged using a
service designed in the runtime environment called the ExternalDataExchangeService.
This service allows you to make calls into a running workflow instance
using events, and to call out from a running workflow using method
calls. In order to hook up the workflow to the host service, it must
contain a class that implements the interface that is intended for
communications. This interface will use the ExternalDataExchange
attribute to signal the workflow designer and runtime that this
interface is intended for communication between the host and workflow.
Once all these code artifacts are defined, the CallExternalMethod and HandleExternalEvent activities can be hooked up to the host service using the WF designer.
Activities
A workflow is a
sequence of activities executed by the workflow engine. An activity
should be modeled as a real-world action required for completing a
parent business process. An activity is a class that encapsulates logic
and can potentially be reused across different workflows.
WF includes several
activities known as the base activity library. Activities from the base
activity library are commonly used with sequential workflows (Table 2).
Table 2. Base library activities commonly used with sequential workflows.
Activity | Description | |
---|
IfElse | allows
conditions to be specified in the workflow and the runtime engine
evaluates each condition and acts upon it based on the result (the IfElse activity can contain other IfElse activities and a default IfElse activity if no other condition is met) | Looping and Synchronization |
While | accepts
a condition and evaluates it at the beginning of every iteration (if
the condition is true, the child activity is run repeatedly until the
condition becomes false) |
Replicator | executes a child activity a given number of times (similar to the foreach statement in C#) |
Sequence | is used to execute a group of activities, one at a time, in a predefined order |
Parallel | executes
two or more sequences of activities in parallel or in an interleaved
manner (all sequence activities must be completed before the workflow
moves to the next activity) |
Listen | is used to idle the workflow process and wait for a wake-up call (the Listen
activity is typically used when human interaction is required—it
serializes the workflow and goes into a passive mode when it is waiting
for human intervention and upon receiving an event, it reactivates the
workflow and continues with the processing logic) | Human Intervention |
EventDriven | is implemented by using the EventDriven activity (a ListenEventDriven activities and child activities that represent human events) activity must contain |
HandleExternalEvent | is invoked when an event specified in an interface is raised (the HandleExternalEvent activity is used by WF to communicate with an external service) |
Delay | is used to suspend the execution of the workflow for a specified amount of time |
Code | allows source code to be injected directly into the workflow (it fires the ExecuteCode event that executes the code, plus this activity can call an external assembly) | Execution |
CallExternalMethod | is
used to call a method in a class available to the workflow (the
interface and its implementation must be available in the same assembly) |
InvokeWorkflow | invokes another workflow to start executing |
InvokeWebService | invokes
a Web service external to the workflow application (creates a Web
reference to a Web service and allows operations on the service to be
invoked) |
WebServiceInput | enables a workflow to receive a Web service request |
WebServiceOutput | pairs with a WebServiceInput activity to respond to a service request (to use this activity, the WebServiceInput activity must be configured first) |
TransactionScope | is used to represent System.Transactions in WF (supports all the properties currently supported by System.Transactions) |
Terminate | is used to terminate the execution of the workflow |
State
machine workflows provide a way of defining workflows that match an
organization’s business process by using states, events, and
transitions to model workflow logic. A state represents a snapshot of
the business process. The workflow is always in one state and will
transition to a new state when it receives an event. Typically, some
action will take place in the outside world for the state in the
workflow to be transitioned to a new state. On reaching the final
state, the workflow is completed.
The base activity library includes several activities designed to enable state machine workflows (Table 3).
Table 3. Base library activities commonly used with state machine workflows.
Activity | Description |
---|
State | represents
a state in a state machine workflow (when an event arrives, the
workflow will transition from one state activity to a new state
activity) |
EventDriven | represents an event handler in a state machine and is placed inside a state activity |
SetState | is used to model transitions in a state machine workflow (includes the TargetStateName property that points to the destination state) |
StateInitializationStateFinalization
| used
to perform pre- and post-processing in a state and run when the state
machine transitions into the state containing the initialization
activity (the StateFinalization activity runs when the state machine transitions out of a state) |
A state machine workflow
is commonly consumed by one or more UI components that must reflect the
current state of the workflow and allow users to only perform legal
events. WF includes the StateMachineWorkflowInstance
class that provides an API to manage and query a state machine
workflow. The class includes properties used to fetch the current state
name and find legal transitions for the state. It also includes
properties that provide a history of all the states the workflow has
been through.
Workflow Runtime Environment
A
workflow instance is created and executed by the workflow runtime
engine. The runtime engine relies on several runtime services for
persisting the workflow’s state, managing transactions, tracking
workflow’s execution, and other features. Each instance of the runtime
engine can support multiple instances of a workflow concurrently. The
workflow instance runs in a host process or in an application domain
and can be hosted on ASP.NET Web sites, Windows forms, Windows
services, Web services, or SharePoint.
The runtime engine is
powered by runtime services that provide an execution environment for
transactions, persistence, tracking changes, timer, and threading.
Runtime services can be augmented by plugging in custom services that
allow changing the behavior of the runtime engine to meet the specific
needs of the execution environment.
For most workflow
implementations, the default implementation of runtime services
satisfies the needs of the execution; however, in some cases the
behavior may need to be altered. For example, the workflow may require
the host application and the runtime engine to communicate differently,
which would require building custom services.
The workflow execution
starts by creating an instance of the workflow. It proceeds with
carrying out activities until it is required to idle the execution, at
which point the instance state is persisted to disk.
WF Programming Model
WF classes are encapsulated in three namespaces, as shown in Figure 3.
Figure 4
illustrates a simple sequential workflow that contains a Calculator
activity. To automate this workflow, the host will need to instantiate
it and provide it with parameters including the input values and the
operation to perform.
In the following example, we show this workflow hosted in a console application:
Example 2.
using System.Workflow.Runtime; using System.Workflow.Runtime.Hosting; namespace WFWorkflow { class Program { static void Main(string[] args) { using(WorkflowRuntime workflowRuntime = new WorkflowRuntime()) { AutoResetEvent waitHandle = new AutoResetEvent(false); workflowRuntime.WorkflowCompleted += delegate (object sender, WorkflowCompletedEventArgs e) {waitHandle.Set();}; workflowRuntime.WorkflowTerminated += delegate (object sender, WorkflowTerminatedEventArgs e) } { Console.WriteLine(e.Exception.Message); waitHandle.Set(); }; WorkflowInstance instance = workflowRuntime.CreateWorkflow (typeof(WFWorkflow.CalcWorkflow)); instance.Start(); waitHandle.WaitOne(); } } } }
|
In
the preceding example, the host process initiates the workflow runtime
and then starts the workflow method itself. The workflow runtime is
started up by instantiating the workflowRuntime class. Passing in the workflow type to the CreateWorkflow method then creates a workflow instance class. The Start method on the workflow instance kicks off the workflow business process.
There are several events raised by the workflow runtime environment. These include WorkflowCompleted and WorkflowTerminated (the latter of which is called when there is an error). The previous example uses anonymous delegates, as WorkflowTerminatedEventArgs provides information on the exception that was generated. When the WorkflowCompleted event is called, we set the waitHandle value.
Passing Parameters into a Workflow Instance
Parameters are passed to a workflow instance using the Dictionary object. The CreatWorkflow operation has an overloaded operation that takes not only the workflow type but also a Dictionary object that accepts parameters. The code fragment in this next example demonstrates this:
Example 3.
Type type = typeof(WFWorkflow.CalcWorkflow); Dictionary<string, object> parameters = new Dictionary<string, object>(); parameters.Add("Value1", 11); parameters.Add("Value2", 19); parameters.Add("Operation", "+"); WorkflowInstance instance = workflowRuntime.CreateWorkflow(type, parameters); instance.Start();
|
Returning Parameters from a Workflow Instance
The WorkflowCompletedEventArgs in the WorkflowCompleted event returns an output parameter, as shown here:
Example 4.
AutoResetEvent waitHandle = new AutoResetEvent(false); workflowRuntime.WorkflowCompleted += delegate (object sender, WorkflowCompletedEventArgs e) { int total = (int)e.OutputParameters["Result"]; Console.WriteLine(total); waitHandle.Set(); };
|
Output parameters are
collections and data is extracted from collection and cast into an
integer. The names of the input parameters passed into the workflow
instance automatically map to the names of properties defined inside
the workflow class. In this case, the Result property maps directly to the result parameter returned. The input properties are used to initialize private variables in the workflow instance:
Example 5.
public partial class CalcWorkflow : SequentialWorkflowActivity { private int value1 = 0; private int value2 = 0; private int result = 0; private String operation;
public int Value1 { set{value1 = value;} } public int Value2 { set{value2 = value;} } public string Operation { set{operation = value;} } public int Result { get{return result;} } private void Calculator_ExecuteCode (object sender, EventArgs e) { if (operation == "+") {result = value1 + value2;} } }
|
Workflow-Enabled Services
Only a workflow that uses the WebServiceReceive activity can be published as a Web service. A simple scenario would be to create a workflow project and add WebServiceReceive and WebServiceResponse
activities to it. In this case, the workflow is activated by calling a
method on the Web service and returning a value when the processing is
complete. In Visual Studio, a workflow project can be published as a
Web service by right-clicking on a workflow project and selecting
“Publish as Web service.” This action will create an ASP.NET project,
with ASMX and Web.Config files.
Versioning Orchestrations
Orchestrations in WF can be versioned in two ways:
1. | Execute the XOML file– In this case, the service reads the .XOML file and creates a Workflow instance using the CreateWorkflow method. The XOML file does not support versioning; the file can be manually versioned.
|
2. | Use assembly level versioning–
Each assembly has a version number, and two assemblies that differ by
version number are considered by the runtime to be different
assemblies. The assembly version can be managed by modifying the assembly.cs file prior to deployment.
|
Note that because with
assembly-level versioning a new version of a workflow is treated as a
new assembly version by the runtime, different assembly versions can
run concurrently.
WF Extensibility
WF provides various
extensibility points that are broad and do not impose semantics on the
user. For example, if we are extending the persistence mechanism, WF
does not stipulate that we use either the data-store or the
serialization techniques.
WF’s extensibility model allows almost every aspect of WF to be extended. Some common extensibility requirements include:
creating custom policies
creating workflow tracking services
adding new activities for persistence, tracking, and communication
creating domain-specific activities
WF has a visual designer used in Visual Studio that can also be extended to create domain-specific designers.
Business Rules
A service-oriented solution
is typically modeled so as to factor out task services because these
services encapsulate business non-agnostic activities specific to the
overarching business process. The WF visual designer can be used to
glue together various business activities that involve business rules
that are exposed in one of two ways:
1. | Conditions
can be used with built-in and custom activities to change their
execution behavior. There are several built-in rules-based activities
that enable conditional logic:
IfElse (provides decision logic) While (provides looping behavior) Replicator (analogous to a for-each statement)
Also supported is the conditioned activity group (CAG) that can provide rules-driven behavior over a collection of activities.
|
2. | PolicyActivity,
a rules engine that comes embedded with WF, can be used with a
specialized workflow activity class that encapsulates a RuleSet which
is stored in a .rules file. At runtime, PolicyActivity retrieves the
rules from the .rules file and executes the rules.
|
The PolicyActivity engine can be used to apply Rules Centralization so as to centralize business rules logic in the RuleSet, effectively establishing a rules service.
|