Testing the Application
Press F5 to test the application. Each time you run the application, $100 will be debited from account 12345 and credited into account 54321. Run the application a few times until the balance of account 12345 becomes 0. When its balance hits negative after the debiting process, you will see the output shown in Figure 16
. This is caused by the Throw activity and the FaultHandlers that you just added.
Figure 16. Examine the output of the workflow to see how the insufficient funds condition is handled.
Figure 17. The modified workflow includes a new TransactionScope and Throw activities.
If you checked the database after this, you will see that the balance of account 12345 remains at $0 and no additional amount of money is credited into account 54321.
So far, all the exceptions in this workflow happen within a transaction. What happens if you realize after the completion of a transaction that you need to roll it back? This process is known as compensation. Continuing with our earlier example, funds may have been successfully transferred from account 12345 to account 54321. But let's assume that later we learned that the account numbers given were not correct. In this case, two accounts must be rolled back to their previous states.
To illustrate compensation, let's modify the existing project.
Replace the existing TransactionScope activity with the CompensatableTransactionScope activity. Also, add a Throw activity to the workflow and place it outside compensatableTransactionScope. Figure 17 shows the new workflow.
Here, I am simply going to perform the transaction and then use a Throw activity to invoke an exception (you can always add your own logic to determine if an exception must be invoked; for now I am just hard coding it).
Right-click on compensatableTransactionScope and select View Compensation Handler. Figure 18 shows that upon selecting the new view, you will see the new compensationHandlerActivity activity.
Figure 18. View the Compensation Handler.
Figure 19. Drag and drop a Code activity into the compensationHandlerActivity activity.
Drag and drop a Code activity into compensationHandlerActivity and name it Compensate (see Figure 19
Double-click on Compensate and code the following:
Private Sub Compensate_ExecuteCode( _
ByVal sender As System.Object, _
ByVal e As System.EventArgs)
Dim conn As New SqlConnection(My.Settings.ConnectionString)
Dim command As New SqlCommand("UPDATE Accounts SET Balance = Balance + 100 WHERE UserID='12345'", conn)
command.CommandText = "UPDATE Accounts SET Balance = Balance - 100 WHERE UserID='54321'"
Console.WriteLine("The transaction has been compensated")
Essentially, this activity will manually rollback (reverse) the transaction. That is, it will credit $100 into the account 12345 and debit $100 from 54321.
In the Fault Handler view of the workflow, add a Compensate activity to faultHandlerActivity1 (see Figure 20) and set its TargetActivityName property to "compensatableTransactionScopeActivity1."
Figure 20. Add the Compensate activity to faultHandlerActivity1.
Figure 21. Examining the output of the workflow.
That's it! When you press F5 to test the application, you should now observe the output as shown in Figure 21
To finish, verify that the balances for both accounts are the same before and after the workflow is executed.
In this article, you have seen a working sample of how transactions are implemented in Windows Workflow. In addition, you have also seen how you can explicitly cause an exception by using a Throw activity. For transactions that have been completed, you need to use compensation to rollback the transactions.
But you can easily how a Workflow can be an elegant and visual interface to transaction-oriented applications.