Microsoft Corporation
June 1997
This article discusses how applications determine the outcome of a transaction—that is, whether the transaction will commit or abort.
First, it is important to understand that the Microsoft® Transaction Server (MTS) objects involved in a transaction do not need to know the transaction outcome—all objects involved in a transaction are automatically deactivated. This causes the objects to lose any state that they acquired during the transaction. Consequently, their behavior is not affected by the outcome of the transaction.
Each object can participate in determining the outcome of a transaction. Objects call SetComplete, SetAbort, EnableCommit, and DisableCommit, based on the desired component behavior. For example, an object would typically call SetAbort after receiving an error from a database operation, on a method call to another object, or due to a violation of a business rule (for example, account overdrawn).
The client of the transaction determines its success or failure (commit or abort) based on values returned on the method call that caused the transaction to complete. The client can be either a base client or another MTS object that exists outside the transaction. The client must know which methods cause transactions to complete and how the method output values can be used to determine success (commit) or failure (abort).
An object method that intends to commit a transaction typically returns an HRESULT value of S_OK after calling SetComplete. On return, MTS automatically completes the transaction. If the transaction commits, the S_OK value is returned to the client. If it aborts, the HRESULT value is changed to CONTEXT_E_ABORTED. The client can use these two values to determine the outcome.
There are two common approaches that can be used by an object method to notify its client that it has forced the transaction to abort (by calling SetAbort):
For example, the Sample Bank application uses an output parameter to indicate failure:
Public Function Perform(lngPrimeAccount As Long, _
lngSecondAccount As Long, lngAmount As Long, _
strTranType As String, ByRef strResult As String) _
As Long
' get our object context
Dim ctxObject As ObjectContext
Set ctxObject = GetObjectContext()
On Error GoTo ErrorHandler
' check for security
If (lngAmount > 500 Or lngAmount < -500) Then
If Not ctxObject.IsCallerInRole("Managers") Then
Err.Raise Number:=ERROR_NUMBER, _
Description:="Need 'Managers' role " + _
"for amounts over $500"
End If
End If
.
.
.
ctxObject.SetComplete ' we are finished and happy
Perform = 0
Exit Function
ErrorHandler:
ctxObject.SetAbort ' we are unhappy
strResult = Err.Description ' return the error message
Perform = -1 ' indicate that an error occured
End Function
It is also important to note that there are failure scenarios where the transaction outcome cannot be determined by the client. This can occur when a call failure occurs due to a transport error (for example, RPC_E_CONNECTION_TERMINATED). An application-defined protocol must be used in such cases to determine the transaction outcome.
Resource managers are guaranteed to get transaction outcomes as part of the two-phase commit protocol (managed by the Microsoft Distributed Transaction Coordinator). This allows resource managers to manage locks (isolation) and to determine whether state changes should be made permanent or discarded.