Determining Transaction Outcome

This section discusses how applications determine whether a transaction will commit or abort.

First, it is important to understand that the MTS objects involved in a transaction do not need to know the transaction outcome. All objects involved in a transaction are automatically deactivated. Deactivation 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 such as an overdrawn account.

The client of the transaction determines its success or failure (commit or abort) based on values returned from 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.

An object method typically notifies its client that it has forced the transaction to abort by calling SetAbort in one of two ways:

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 client cannot determine the transaction outcome. This situation results, for example, when a call failure occurs due to a transport error such as RPC_E_CONNECTION_TERMINATED). In such cases, it is necessary to use an application-defined protocol to determine the transaction outcome.

On clustered servers, MTS will not automatically reconnect to MS DTC in the event of a failover. Not enough information exists about the transaction composition and state to determine the appropriate course of action. Retries remain the responsibility of the client application. The client cannot differentiate an error caused by failover from other errors.

Resource managers are guaranteed to get transaction outcomes as part of the two-phase commit protocol managed by the Microsoft Distributed Transaction Coordinator. This feature allows resource managers to manage locks and to determine whether it is necessary to make state changes permanent or to discard them.