At first glance Windows
Workflow (WF) and .NET Task Parallel
Library (TPL) may appear to be competing execution engines. Both emphasize
composing work and parallel execution. While the tools share some
characteristics, each tool is very different. TPL is a lower-level library for
managing class level workloads and WF is architected around Activity building
blocks with User Interface design features as well as work composition. In
fact, had TPL been available when WF was built the WF team could have seriously
considered building WF on top of TPL.

Even though WF was not built on TPL, there are TPL features
that can make running WF workflows easier. In self-hosted scenarios, for
example, a desktop application may be running a workflow. Code examples in
this article will demonstrate how TPL and WF can work together.

Why a Workflow and a Task?

WF was built for a variety of hosts. So, a developer is not
confined to a Server Host like, for example, AppFabric to run WF workflows. The WF Windows
Presentation Foundation (WPF) based design experience means a developer can
build a design experience around a customizable feature inside, for example, a
desktop application.

Because WF includes a design experience as well as customizable
Activities; developers requiring application user customization find that
workflows are ideal. For example: an application that must perform custom
printing could include a set of Printing Activities. Application users could
mix and match the set of Print activities depending on the need.

Starting with .NET 4.0 Tasks underpin the Concurrency and
Asynchronous .NET
Framework unit work standard. Tasks enable capabilities like Continuations. Tasks can loosely couple the result of a Task completion to some other
components in an application. For example, in the printing scenario above; a Task
can tie a User Interface update to a completed workflow.

As stated earlier, outside of using a server like AppFabric;
WF includes a number of options for running a workflow.

Self-Hosting WF

Unless a developer is self-hosting a Workflow Services
workflow; there are two techniques for invoking a workflow inside an
application.

The technique runs the workflow synchronously. Invoke
exits when the workflow completes. WorkflowInvoker is great for unit testing a
workflow. Most self-hosted applications will need more control over an
executing workflow. For example: most applications would probably require some
sort of workflow abort. A more powerful approach involves the
WorkflowApplication class.

WorkflowApplication

WorkflowApplication gives a developer almost complete
control over a running workflow. A developer can hook events like workflow
exceptions and completions. Workflows can be aborted and workflow exceptions
can be handled gracefully.

The sample code below leverages the WorkflowApplication
class along with a number of other classes.

WF workflows are Trees of Activity classes. WorkflowApplication requires a reference to the root of the workflow Tree.

Workflows can be compiled and included in a .NET assembly or
the workflow can be loaded directly from an XML file. Sticking with the
Printing scenario introduced earlier in the article, an Application user-generated
workflow would likely be saved in an XML format. ActivityXamlServices includes
methods to parse and load the workflow.

The WF runtime executes the workflow in a separate Thread.

Fundamental TPL classes called TaskCompletionSource and the
CancellationToken give a developer control over a Task class Result property.

TPL Classes

Using TaskCompletionSource and the appropriate WorkflowApplication
Event the sample code populates the Task.Result with a value. A complete
review of TaskCompletionSource is beyond the scope of this article, but there
are helpful resources at the end of the article.

The sample code demonstrates Waiting on a Task. However,
making a Task available to a developer gives a class consumer a range of
composition options. For example: Task.WaitAll could wait for a group of executing
workflows to complete and, as mentioned earlier, a Continuation can update a
component on the User Interface.

Aborting a workflow is handled by a CancellationToken
class. When the CancellationTokenSource.Cancel is called a delegate registered
with the token aborts the workflow instance.

Conclusion

The Task class can improve self-hosted workflow execution. Tasks can be manipulated with the TaskCompletionSource and CancellationToken. Making a Task available to a component consumer gives a developer a range of composition options.

About the Author

Jeffrey Juday

Jeff is a software developer specializing in enterprise application integration solutions utilizing BizTalk, SharePoint, WCF, WF, and SQL Server.
Jeff has been developing software with Microsoft tools for more than 15 years in a variety of industries including: military, manufacturing, financial services, management consulting, and computer security.
Jeff is a Microsoft BizTalk MVP.
Jeff spends his spare time with his wife Sherrill and daughter Alexandra.