Mann Software

 

We'll handle this one with an excerpt from the book:

The functionality provided by fault handling in our workflows is no different from what you expect in typical .NET applications: you try something and then catch any errors and handle them. The implementation, however, is turned 90 degrees from what you would expect. Starting from the top, fault, or error, handling in our workflows is going to have the same capabilities as any other .NET application:

  • Faults can be caught within a local scope, or globally for the entire workflow.
  • Unhandled exceptions will cause the workflow to immediately halt execution and shut down.
  • Handled exceptions can be processed and allow your workflow to either continue processing or shut down gracefully, with as much logging or notifications as you need.

We'll cover the details on these as we go through the rest of this section. First, let's review some of the mechanics of how you configure fault handling in workflows. The primary construct for handling errors is the FaultHandler activity. We'll walk through using this activity in just a bit, but at a high level, each FaultHandler activity is associated with one particular type of exception and contains other activities that dictate how the particular error is handled.Like any other error handling, the goal of fault handling in WF is to trap errors as they occur, respond according to our business rules, and ideally allow the workflow to continue processing. If continuation is not possible, we need to clean things up and return our documents or other content to a stable, manageable state.At a global level, exception handling can be applied to the entire workflow. To do this you are going to make use of the three little tabs (shown in Figure 9-4) at the bottom of the Workflow Designer pane in Visual Studio that we haven't paid much attention to as yet. Specifically, we're going to focus on the rightmost tab.

Figure 9-4. The tabs at the bottom of the Workflow Designer in Visual Studio provide access to the fault handling functionality.

Clicking on that tab will reveal the Workflow Exceptions design canvas, shown in Figure 9-5. Even the most cursory glance will reveal that the Exceptions design canvas looks nearly identical to the Workflow Designer we've worked with so far. That is not an accident. Configuring our exception handling is just configuring another part of the workflow. Looking at Figure 9-5 a little more closely, you'll see it consists of a single composite activity of type faultHandlersActivity. This composite, like all of the others we've seen so far (Sequence, Parallel, While, IfElse, etc.) is made to serve as a container for other activities. This one is a little special, though, because it is made to store only one type of activity: the FaultHandler activity mentioned earlier.  If you try to add any other type of activity to it, the Designer will not let you.   If you try to add any other type of activity to it, the Designer will not let you.

Figure 9-5. The Workflow Exceptions design canvas looks remarkably like the regular Workflow Designer canvas we've already worked with.

One other thing that is different about this composite activity is the manner in which it displays its children. Because each of the children is another composite activity, the tree display could get very ugly, very fast. To help alleviate this, Microsoft has coded the faultHandlersActivity to display its children in a unique way - essentially each FaultHandler gets its own design canvas. Each FaultHandler child dropped onto the faultHandlersActivity parent will be displayed in the small ribbon-like construct at the top of the activity. The two blue arrow icons allow you to scroll back and forth between the FaultHandler activities that have been added. The main body of the activity below this will display the child activities from the currently selected FaultHandler.That's a little confusing. Let's walk through an example to help clarify things:

  1. Open or create any workflow.
  2. From the designer canvas, click the right-hand tab located at the bottom of the screen. This will open the Workflow Exceptions design canvas.
  3. From the Toolbox, drag two FaultHandler activities and drop them onto the faultHandlersActivity on the canvas
  4. Click on the first FaultHandler activity—indicated by this icon on the left side: 
  5. Drag a Code activity and drop it into the body of the faultHandlerActivity1 activity - where the small green plus sign has appeared.
  6. Click on the second FaultHandler activity - indicated by same icon shown in step 4, this time on the right side. Notice that the Code activity has disappeared. This is because we are now working with a different FaultHandler activity.
  7. Drag an IfElse activity from the ToolBox and drop it into the body of the faultHandlerActivity2 - again where the small green plus sign has appeared. Add another Code activity to the left-hand branch.
  8. Click back on the first FaultHandler - you'll see that the Code activity is still in place.

If you click back and forth between the two FaultHandler activities, you'll see that everything is still intact - you just have two totally distinct environments in which to handle errors. This, of course, begs the question of why you need separate environments. The answer is that you need multiple environments in order to explicitly handle different types of errors. It is the equivalent of the code shown in Listing 9-1.

Listing 9-1. The Code Equivalent for Our Workflow Fault Handling Constructs

try

{

//do something

}

catch (System.NullReferenceException exNull)

{

//handle a null reference exception

}

catch (System.ArithmeticException exArith)

{

//handle an arithmetic exception

}

catch (System.Exception ex)

{

//catch-all to handle any other error

}

 Each FaultHandler activity represents one of the catch blocks in Listing 9-1.

Configuring Fault Handlers

There are two ways to configure fault handling for our workflows - declarative or imperative. The imperative type of fault handling requires setting one set of properties, creating variables, and writing some code to handle the error however we need to. We'll show this option in a minute. Declarative handling is slightly different. When we handle our workflows declaratively, we work exclusively in the Designer - adding activities to the canvas and setting properties to handle our exception. We don't write any code. We'll cover this next.

Declarative Fault Handling

Declarative fault handling is all about adding activities to the canvas and configuring them to handle the exception according to your business rules. One of the primary distinctions of this approach is that it is somewhat generic in nature; the only real information you know about the fault is its type—and that only because, as we saw earlier, each FaultHandler is configured to handle a particular type of error.So, how do we work with this style of fault handling? As you would expect, it's really pretty straightforward if you're comfortable with the Workflow Designer.

  1. From the Exceptions tab in the Workflow Designer, drag a FaultHandler activity out onto the canvas.
  2. Make sure that the FaultHandler activity is selected and open the Properties window.
  3. Inside the Properties window click on the ellipsis for the Fault Type property. This will bring up a dialog box, shown in Figure 9-6. This dialog box allows you to specify which type of error this particular FaultHandler activity will take care of.
  4. Once you've specified the type of error to be handled by this FaultHandler activity, you can add whatever activities you need to in order to handle the error.
  5. If you need to handle more than one exception type, drag another FaultHandler activity out and configure it in the same way.

OK, I have to jump in here and editorialize a bit. I don't understand the usefulness of declarative fault handling (except for one circumstance, which I'll get to in just a minute). Here's the problem I see - if I'm catching and handling an error, I want to be pretty specific about the way I handle it. Also, in most cases, the error being thrown is likely unique to the workflow that is running. Because of these two points, I need my handler to be specific, which means that I'm likely adding a Code activity and writing some code. Once I've crossed that line, I'm into the realm of imperative fault handling. The only exception I see to this is if all you are doing is simple logging. In that case, write a custom, generic logging activity that will write the information to whatever logging medium you require, add it to the Designer for your fault handler, and you're good to go. No coding required, but you still get some useful information and didn't have to write any code specific to this handler.  Other than this one case, I recommend that you read on and understand the benefits of imperative fault handling—it's a little more work but well worth it for the extra value, as you'll see.

Figure 9-6. This dialog box indicates which type of exception is handled by our FaultHandler activity.

Imperative Fault Handling

Imperative fault handling, as I alluded to earlier, is a little more involved than simple declarative handling. However, it gives us a lot more control and a lot more capability to manipulate our handler process and to be specific with how we process our faults. Imperative fault handling makes use of one new property of the FaultHandler activity: the Fault property. The Fault property allows us to create a property that will be used to store the object representing the exception that was thrown by our workflow. With that, we can then handle our exception however we need to, typically within a Code activity. When the exception is thrown, we will have access to the entire Exception object through the variable referenced in the Fault property.To make this work, follow these steps:

  1. Repeat steps 1 - 3 from the previous section.
  2. Right-click on the FaultHandler activity and select Promote Bindable Properties from the context menu.
  3. Take a look in your code-behind file and you'll see that a new dependency property, named <Fault_Handler_Activity_Name>_Fault1, has been created for you. For example, if your fault handler is named faultArgExcep, then the dependency property will be called faultArgExcep_Fault1. The new property will have a type of System.Exception. If you choose to, you could make the type for this variable more specific.
  4. You'll still need to fill in the FaultType property to tell the workflow runtime which type of error this particular handler is taking care of. For this example, you can type in System.Exception.

That's it. If your workflow ever triggers this handler, your dependency property will store reference to the exception that was thrown. You can drop a Code activity onto the canvas, reference the dependency property, and do with it whatever you need to do in order to handle his error. The main benefit is that you have a lot more information about the error available to you. To harp on logging again—placing some of this information (stack trace, source, etc.) into our logging is going to make errors much easier to troubleshoot.

Local Scope

So far, we've looked at fault handling at what is essentially the global level. We've created handlers for faults that bubble up, unhandled, to the top of the chain - the workflow itself. One definite problem with this is that when you are capturing errors at the workflow level, there is no way to continue the workflow after the error has been handled. Faults caught at the workflow level always immediately precede the termination of the workflow. This is obviously less than desirable. So, what can we do about it? Fortunately, there is an option. Faults can also be handled at the level of any composite activity, so the Parallel, Repeater, Sequence, and While activities (just to name a few) can all handle faults at a more granular level.Handling the errors at the level of a composite activity is exactly the same experience as we saw at the global level. The only thing that is different is how you access the fault handler design canvas for a specific composite activity—simply right-click on the activity itself and select View Fault Handlers from the context menu. That's it. The rest is identical to what I described earlier.

TipWith the scope issues discussed in this section in mind, I recommend that you make judicious use of some of the composite activities that ship with WF. Certainly the Parallel, IfElse, and While activities have their place by performing a specific function in addition to being composite activities. There is, however, the Sequence activity, which can be used when you are executing activities serially but still need to be able to handle faults locally.

If an error is not explicitly handled at a local scope, it will bubble up to the global handler to attempt to be handled there. If the global handler does not take care of the error, the workflow will error out with unpredictable results - including potentially leaving your documents or tasks in unknown states.

Fault Handling Summary

So now we have a pretty good idea of how we go about handling faults in our workflows. The only thing that I'd like to conclude this section with is a brief discussion of how you should handle errors in your SharePoint workflows:

  • As I alluded to previously, you should always log the error. Whether this is with code or a logging activity is entirely up to you. I'll just leave the recommendation at this: log it somewhere.
  • Local, local, local. Whenever possible, handle your faults as locally as possible.
  • Always, always provide a catchall fault handler at the workflow level. Add a FaultHandler activity at the workflow level and configure it to catch exceptions of type System.Exception. If it does nothing else, log the error details somewhere.
  • If tasks were already assigned, you should go back and either delete them or flag them as inactive. Specific details around this would be dependent on your particular business requirements, but it wouldn't do much good, and could potentially be very confusing, if participants had tasks assigned to them for a workflow that had errored out.

CautionIf you are going to undo changes in your workflow because of an error, you should understand how workflows process operations in batches and do some experimentation with your particular situation. Go back and read the section "Workflow Processing" earlier in this chapter before proceeding too far down this road. Specifically when your workflow will commit operations to the database is unique to every workflow. Keep this in mind as you plan your fault handling.

Last modified at 7/29/2009 10:15 AM  by Dave 
SharePoint Server MVP Community Kit for SharePoint Philly Office Geeks ISPA