Windows Workflow and Timeouts
Workflows make it easy to do something that's very hard to do in any other development environment: Take action when something doesn't happen. Here's how to protect yourself when something's isn't happening in your Windows Workflows.
In an earlier column, I pointed out that not all processes have response times measured in seconds; some processes have response times measured in hours, days or even weeks. Those processes issue a call to some other process (typically requiring human processing), and wait for a callback with the results from the other process (or just a signal that the other process is complete).
For those kind of long-running processes, I described how to use Windows Workflow (WF) to create Web Services that automatically handle having to wait. But what if you don't get that response? How will you know when your workflow is stalled? With WF, sending a notification when something doesn't happen is not only easy to do, but also easy to customize.
Playing Defense and Offense
There are two ways to address this problem: defense and offense. Defense means having your workflow, before every activity where the workflow might get stalled, log the activity it's about to perform. As part of the log record, you should include some set of information (information that everyone involved will know) that will allow you to select all the log entries for any particular instance of a workflow. When logging activities in a vacation request workflow, for instance, you might use a combination of the ID for the employee requesting the vacation and the vacation's requested start date to identify any instance of the workflow (presumably no single employee would simultaneously submit two requests for vacations starting on the same day).
Logging the activity if you're working in SharePoint is easy: you have access to built-in logging and email Activities. If you're not working in SharePoint, this might be the time to create a custom activity that you can use to send email. If you're doing that, consider creating a service with methods to accept notifications from your workflows and handle them appropriately; this lets you centralize logging and messaging for all your workflows.
Now, when someone notices that an instance of the workflow has stalled, you can use your log file to determine the last activity performed. That should, in conjunction with some documentation on the workflow's design, allow you to determine why the workflow's stalled. In my experience, it's very difficult to do too much logging in your workflows.
In the same vein, after every activity, you should also send status e-mails to the person most interested in the successful completion of the workflow (for the vacation request workflow, that would be the person requesting the vacation). That person is most likely to notice if the emails stop coming, because the workflow is no longer moving towards its conclusion.
But the fundamental problem is that you really want to send a notification when something doesn't happen -- when an activity that should be performed has gone too long. Essentially, you want to introduce the concept of "timing out". A timeout is "something that happens" when something else doesn't happen. WF makes it easy for you to create the timeout.
As an example, I created a WCF Workflow Service Application project. A Workflow Service starts with two activities already included: A Receive Activity that accepts requests from clients ("consumers", in SOA-speak) and a SendReply Activity that returns an acknowledgment to the client that the request has been received and the workflow started. In one of these Activities, you should log that an instance of your workflow has started. At the end of the workflow, you'll need to add a SendAndRecieveReply Activity that will call some service at the client to return the result from the workflow. In the RecieveReply Activity, you'll log the successful completion of your workflow (you can't do enough logging).
For any "stallable" workflow task, drag in a Parallel Activity, then drag two Sequence Activities into it. WF will automatically set up the Sequences up as tasks to be performed in parallel. You'll put the Activities that make up your workflow task in one Sequence; in the other Sequence, you'll define your timeout. Before the Parallel Activity, log that you're entering the task, and after the Parallel Activity send a status email to the interested party.
The first thing to handle is the scenario when your tasks Sequence finishes in a timely fashion. When that happens, you'll want to terminate the timeout Sequence. To support that, right-click on the Parallel Activity and select Create Variable. In the variable list that pops up, add a new variable of type Boolean, and set its default value to False. Then drag an Assignment activity to the end of the Sequence that holds your workflow tasks. In that Assignment Activity, set your timeout variable to True so that, when your workflow tasks complete, your timeout Sequence will be quietly wrapped up.
In the Sequence that will handle the timeout, add a Delay activity as the first activity and set its Duration for the amount of time you're willing to wait for your tasks Sequence to complete as a timespan. If you're willing to wait one day, for instance, you'd enter this:
Following the Delay, add a log Activity to post a notification that that task timed out.
Now you get to customize your timeout. You may want to terminate the Workflow if the task times out -- if so, drag a TerminateWorkFlow activity into the Sequence after the logging activity.
You may just want to use the timeout notification to tell you that the task wasn't performed and continue on to the next set of tasks in the workflow. If so, add a duplicate of the Assignment activity that sets your timeout variable to true after the logging activity; when time runs out, it will be your workflow Sequence that's quietly wrapped up.
Alternatively, you may just want to nag someone about responding to the workflow sequence and continue to wait for the workflow sequence to complete. In that case, put your Delay and a logging activity in a Sequence inside a DoWhile Activity that has a condition that will never be true. You'll get your notification that the workflow Sequence hasn't completed at regular intervals until the workflow sequence completes (at which point, your timeout sequence will automatically be wrapped up).
You can do more: adding code to your Delay will let you set the duration dynamically at runtime, for instance. But by using WF, you have all the tools you need to take action when something doesn't happen.
Peter Vogel is a principal in PH&V Information Services, specializing in ASP.NET development with expertise in SOA, XML, database, and user interface design. His most recent book ("rtfm*") is on writing effective user manuals, and his blog on technical writing can be found at rtfmphvis.blogspot.com.