Last year Microsoft released the .NET Framework 3.0. Unlike previous releases of the framework this version was not an upgrade or change to existing functionality but it was shipped as a number of additions to the existing .NET Framework 2.0. One of these additions was Workflow Foundation (WF), the others being Windows Communication Foundation (WCF), Windows Presentation Foundation (WPF) and Windows CardSpace. The goal of this article is to provide an overview of what Workflow Foundation is about and when or where its use would be advantageous.
Quite a lot of software is developed for a business purpose. This should hardly be a surprise, since this business is what pays our salaries, and has been true for most of the time that computers have existed. Something that is changing is the complexity of the software being developed. This may be because of a number of reasons like increasing complexity of business or the increased possibilities due to connected and more powerful computers. Whatever the reason, we as developers are tasked with developing these increasingly complex systems and to do so we need an increasingly powerful toolset. Workflow Foundation aims to be part of this toolset by helping us model business logic in a more abstract and graphical way. One major advantage of this graphical way is that it enables us to show a business owner what an application is going to do, something we used to do with diagrams. The big difference between Workflow Foundation and the previously used diagrams is that the workflow shown actually executes. In the case of design diagrams these are just pictures showing the intent instead of what is actually done. This intent was then implemented by the developer, resulting in a lot of source code and ultimately an executable, but if this was implemented differently this was hard to spot, especially for a business person. One reason this was difficult to spot was the fact that the intent, or business person’s need, was intermingled with the implementation code or the application’s needs.
Figure 1. Sequential workflow describing a lawsuit
Workflow Foundation hopes to change all this by separating the intent from the implementation details. The intent is expressed in a graphical way, something the business person can understand. The implementation is still done in plain old-fashioned Visual Basic code but its execution is orchestrated by the graphical business layer. Let’s take a look at Figure 1. This shows the process of how a lawsuit in the USA is supposedly run according to some people; luckily I have never personally experienced this. The process shows all the steps that need to be done like hiring lawyers, paying them and having them write petitions to each other and the courts. We can ask a legal expert to check this and he might conclude this is right. This means we have covered the business goals and needs in our application. Next we can go ahead and implement the actual code needed to perform all these business actions. This will introduce a lot more complexity into the application but will not change the workflow as it is displayed to the user.
Maybe this sounds familiar, and it should. Before we had graphical form designers we would sketch the user interface on a piece of paper for the user to look at. They would confirm, probably after a number of changes, that this was what they needed and we would go ahead and build it. This would involve writing lots of tedious code fragments containing screen layout as well as the business data involved. Next, the user would test the working form and decide that something needed to be arranged a little differently. Out comes the paper again to update the sketches, and a developer starts going through all the code to figure out how to move all the controls. The arrival of graphical user interface designers like the Windows Forms designer made this a lot easier. The developer could just drag controls around building the required design and later, in code, focus on the business side of things. This is very similar to what the workflow designer is doing for us in the case of business processes.
One of the goals in software engineering is the separation of interface and implementation. This has been a goal for a long time and is, for example, very apparent in either COM or Windows Communication Foundation. Up until now there often was a separation between the interface and the implementation in business code but, because both where expressed in source code, this was of limited benefit to businesses. After all, a business analyst could hardly be expected to understand the Visual Basic source code used to describe the contracts. With Workflow Foundation all this changes. Now, using Workflow Foundation, we have a clear separation between the process, or contract, and the actual implementation. The first part, being the structure of the business process, is coded in a workflow. The result is a very visual schema describing the process flow. Because of its visible nature this workflow is quite easy to understand for a business analyst. After all, they draw flowcharts all the time to model the same process flow. Of course the actual implementation still has to be coded and this is still done using regular Visual Basic code, either through custom activities or in external services. Because the graphical workflow actually executes and controls the various implementation parts a previously huge communication problem is considerably smaller.
Lots of business processes are long running. Just take a look at an order being entered into a system and all the steps it has to go through:
All together this is quite a bit of work and something that takes some time. In the past we would have to manage all this and the associated state ourselves. Workflow Foundation can be configured to work with a persistence service that automatically saves the state of a workflow to disk. This saved state means that the process or even the machine can be restarted and the workflow will continue where it last stopped. Even better, because the default implementation saves the workflow state inside of SQL Server we can even resume the workflow in a completely different machine if we would prefer.
If we look at the previous example we can see there are a number of things, like send a reminder, we have to do when an expected event doesn’t occur within a certain period of time. In the past this would mean saving this fact somewhere like a database and having our application check on some kind of interval if a deadline has expired. When we are using Workflow Foundation there is no extra work involved. All we need to do is include a ListenActivity with two branches. The first branch waits for and contains the code that needs to be executed when the expected event occurs. The second branch contains a DelayActivity and contains the code that needs to be executed when the event didn’t occur before the specified time elapsed. The ListenActivity is the one being executed in this case and its sole purpose is to determine which of its child branches needs to be executed with all others being cancelled.
Figure 2. ListenActivity with a DelayActivity as a timeout
Creating and running these long running workflows is one part of the problem that needs to be solved. Another problem is that users often want to know how far along a specific process or workflow is. This is where workflow tracking becomes really useful. Workflow tracking is an optional service that can be added to the workflow runtime whenever needed—a good thing because it means there is no overhead if tracking isn’t required. The tracking database (the default implementation uses SQL Server) can be used as the basis to display to the user what the workflow has done and when. One interesting way of displaying this is by using the same workflow designer used to create workflows. In fact there is a very nice sample that shows how to go about and do this. The sample application is located in the MSDN Library at http://msdn2.microsoft.com/en-us/library/ms741706(VS.85).aspx. I mentioned that the default tracking service uses SQL Server and it is named SqlTrackingService, but it isn’t hard to create one using another database or even a completely different type of storage media. In fact, this is a recurring theme with runtime services where Workflow Foundation provides developers with a base class and one or two implementations, leaving the developer, or third-party market, to create more.
Getting started with your first workflow is simple. Start by opening Visual Studio 2008 or 2005. If you are using Visual Studio 2005 you will first need to install the Visual Studio 2005 Extensions for Windows Workflow Foundation, which can be downloaded from http://msdn2.microsoft.com/en-us/windowsvista/aa904955.aspx. Select New Project from the File menu and navigate to the Workflow node in the tree view inside the Visual Basic node. Here we find the various workflow project types. Let’s create a sequential workflow by choosing the “Sequential Workflow Console Application,” entering a name and clicking OK. Visual Studio now creates a new workflow project with a console application as the host and displays the default workflow in the designer. Now open the Toolbox if it isn’t open yet and drag a CodeActivity, called “Code” in the toolbox, onto the workflow. You need to drop it on or near the line between the green circle at the top and the red square below. This green circle with the arrow is where the sequential workflow will start executing and it will continue running until it reaches the red square or something goes horribly wrong and an exception goes unhandled.
Figure 3. Building your first workflow
The CodeActivity1 we just dropped on the workflow contains a red exclamation mark at the right top indicating it isn’t valid yet. This is because it wants to execute some code and it doesn’t have any yet. Double-click on CodeActivity1 to create the codeActivity1_ExecuteCode() event handler. Go ahead and enter some code. Let’s be original and enter: Console.WriteLine("Hello Workflow!"). Now press CTRL+F5 to see the workflow run and print the message. If you hit F5, or click the debug button on the toolbar, the message will appear but, because the workflow is done, it will not be readable as the application terminates immediately.
When we want to execute a workflow we first need to create a workflow runtime. This workflow runtime is a standard .NET object so we need an application of some sort with an AppDomain as a host. In the previous example we created a basic console application as the host but this could be something very different as well. For example it is possible to host the workflow runtime inside of ASP.NET to power the business rules of an e-commerce site, or inside of a Visual Studio Tools for Office (VSTO) add-in hosted in Excel calculating some complex discounts.
Take a quick look inside the Module1.vb inside of the previous sample. Inside of the Sub Main we see an object of type WorkflowRuntime being constructed. As this is a very simple project there is no extra configuration but quite often this will be followed by calls to workflowRuntime.AddService() to add extra runtime services. Another way to configure these runtime services is by using the application configuration file or app.config. The next thing we see is the addition of two event handlers. These aren’t required but are used to track that the workflow is done. The WorkflowCompleted event signals a correct completion of a workflow while the WorkflowTerminated event signals an abnormal completion of the workflow. Besides these two events the workflow runtime exposes a number of other events allowing us to track the running workflows.
Shared WaitHandle As New AutoResetEvent(False)
Using workflowRuntime As New WorkflowRuntime()
AddHandler workflowRuntime.WorkflowCompleted, _
AddHandler workflowRuntime.WorkflowTerminated, _
Dim workflowInstance As WorkflowInstance
workflowInstance = _
SharedSub OnWorkflowCompleted( _
ByVal sender As Object, ByVal e As WorkflowCompletedEventArgs)
SharedSub OnWorkflowTerminated( _
ByVal sender As Object, ByVal e As WorkflowTerminatedEventArgs)
Listing 1. The Module1.vb in a new project
To create an instance of the workflow itself the workflowRuntime.CreateWorkflow() function is used. This function loads the workflow definition and returns a wrapper workflowInstance object to work with. Because the workflowInstance object is not of the workflow type specified we cannot set business properties on it. Instead we would need to create a dictionary with name/value pairs and pass that to the CreateWorkflow() function. Finally we need to start the workflow by calling the workflowInstance.Start() function, something which automatically starts the workflow runtime as well in this case.
Because workflows run asynchronously the WaitHandle.WaitOne() is used to make sure the application waits for the workflow to terminate. This is something that is only done in a simple application like this and should never be done in a real application. Normally some other mechanism is used to control the lifetime of the workflow runtime and the StartRuntime() and StopRuntime() functions will be used to start and stop the runtime.
Workflows usually don’t live in their own private little world and have to communicate with other applications or objects inside of the application. There are several ways to do this but the easiest to get started with, and in fact a quite powerful way, is the combination of the ExternalDataExchangeService with a CallExternalMethodActivity and/or HandleExternalEventActivity.
Using workflow runtime As New WorkflowRuntime()
Dim edex As New ExternalDataExchangeService()
Dim demo As New DemoDataExchange()
Listing 2. Adding an ExternalDataExchangeService to the workflow runtime
The ExternalDataExchangeService class enables a developer to add data exchange services to a workflow and is part of the workflow runtime. To create a service that can be used this way the first step is to create an interface decorated with the ExternalDataExchange attribute. The service type that implements this interface is added to the ExternalDataExchangeService object.
Public Interface IDemoDataExchange
Sub SendSomeData(ByVal theData As String)
Event ReceiveSomeData As EventHandler(Of ExternalDataEventArgs)
Public Class DemoDataExchange
Public Event ReceiveSomeData( _
ByVal sender As Object, ByVal e As ExternalDataEventArgs) _
Public Sub SendSomeData(ByVal theData As String) _
Dim state As New SendSomeDataState( _
ThreadPool.QueueUserWorkItem(AddressOf InternalSendSomeData, state)
Private Sub InternalSendSomeData(ByVal stateObj As Object)
Dim state As SendSomeDataState = stateObj
Dim reply As String = Console.ReadLine()
Dim args As New ReceiveSomeDataEventArgs(state.instanceId, reply)
RaiseEvent ReceiveSomeData(Nothing, args)
Private Class SendSomeDataState
Public instanceId As Guid
Public theData As String
Public Sub New(ByVal instanceId As Guid, ByVal theData As String)
Me.instanceId = instanceId
Me.theData = theData
Listing 3. The ExternalDataExchange interface and its implementation
Inside of the workflow we can now use the CallExternalMethodActivity to call the SendSomeData() function inside of the service by dragging an instance onto the workflow. The first thing we need to set is the InterfaceType property. Click on the ellipses button in the property sheet behind the interface type. Doing so should open a type browser showing all interfaces decorated with the ExternalDataExchange attribute. Select the IDemoDataExchange and click OK. Next we need to set the MethodName property to the actual function to call. Select SendSomeData from the drop-down, and the CallExternalMethodActivity is good to go. Using a HandleExternalEventActivity is very similar to the CallExternalMethodActivity. Start by dragging a HandleExternalEventActivity onto the workflow just below the CallExternalMethodActivity and setting the InterfaceType the same way. To receive a call we next need to specify the EventName property that is going to fire. In this case select ReceiveSomeData from the dropdown and the HandleExternalEventActivity is also in business.
Figure 4. Using an ExternalDataExchange service
Workflow Foundation is a powerful new addition to the .NET Framework. While Workflow Foundation may not be usable in every application there are quite a few that do benefit. I believe every .NET developer should have at least a basic knowledge of its strong and weaker points so he or she can decide if Workflow Foundation is applicable for their applications.