With WF, compensation logic can be defined via the
graphic design by dragging-and-dropping activities, just as you would
define regular workflow logic. WF compensations not only support common
transaction semantics, they can be also used to clean up the programming
model. All code necessary to run compensations is grouped in
compensation handlers (similar to catch()
blocks in C#). The main difference with compensation handlers is that
they run when an error occurs after a block of operations has already
executed and completed successfully.
Creating Compensations
Other than transactable
resources in the context of ACID transactions, compensatable resources
must provide explicit access to the compensation logic. WF 3.0 supports
the application of Compensating Service Transaction for one or more workflow activities through the CompensatableSequence and the CompensatableTransactionScope activities. Each has an associated compensation handler that runs when:
the compensatable scope
completed successfully (for example, without throwing an exception),
but the orchestration failed with an unhandled exception
the compensatable scope completed successfully, but a Compensate activity triggered the compensation
By default, the WF 3.0 process
designer does not show compensation blocks to avoid any distraction
from the main process flow. You can switch the designer view from the
process flow to the compensation flow by clicking on the compensation
handler icon.
WF 4.0 introduces some changes to give you more granular control over the execution of compensation logic. The CompensatableActivity allows compensation of the embedded activities when certain conditions are met. The activity’s compensation handler runs when:
the
compensatable scope completed successfully (for example, without
throwing an exception), but an unhandled exception occurred afterwards
and the executing WorkflowApplication’s OnUnhandledException handler returned UnhandledExceptionAction.Cancel
the compensatable scope completed successfully, but a Compensate activity explicitly triggered the compensation (this is true unless the CompensatableActivity has been confirmed with the Confirm activity)
The Confirm activity in WF 4.0 can prevent a completed CompensatableActivity
from running the compensation logic even when compensation is triggered
explicitly. This lets workflows mark certain tasks as irreversible,
even if a Compensate activity triggers compensation at higher levels.
Triggering Compensations
Note also that
you need to trigger compensations explicitly when exceptions are handled
in any part of the workflow. Error handlers (such as Fault Handlers in
WF 3.0 and Catch activities in WF 4.0) need to invoke the compensation
handlers with the Compensate activity.
In WF 3.0, the
Compensate activity can only be placed inside a Fault handler or a
CompensationHandler. In WF 4.0, you can place Compensate activities in
the regular flow of a workflow, but the activity expects a valid
CompensationToken. These tokens are only set once a
CompensatableActivity completes successfully. Calling Compensate,
without a valid token, results in an exception.
Compensate also raises an exception if you invoke it for a CompensatableActivity
that has been explicitly confirmed with a Confirm activity. Therefore,
it’s important to wrap Compensate activities in a TryCatch activity.
Note
WF also supports Atomic Service Transaction via two-phase ACID transactions inside workflows. A TransactionScope or a CompensatableTransactionScope can define the scope of a .NET transaction, as provided by the System.Transactions.Transaction namespace.