Voting on a Transaction

As mentioned before, a transactional object votes whether to commit or abort the transaction by setting the value of a bit in the context object. That bit is called the consistency bit. The name is appropriate. Consistency is the only transaction property under the application’s objects control. COM+ can manage atomicity and the resource managers guarantee isolation and durability, but only the objects know whether the changes they make to the system state are consistent or if they encounter errors that merit aborting the transaction.

When COM+ creates a transactional object, it puts it in its own private context and sets the context object consistency bit to TRUE. As a result, if the object makes no explicit attempt to set the consistency bit to FALSE, the object’s votes to commit the transaction.

Tip

An object can actually share its context with other objects whose transaction setting is set to Disabled.

The object can set the value of the consistency bit by accessing the context object and getting hold of IContextState interface, defined as:

enum tagTransactionVote
{
   TxCommit= 0,
   TxAbort = TxCommit + 1
}TransactionVote;

interface IContextState : IUnknown 
{
   HRESULT SetDeactivateOnReturn([in] BOOL bDeactivate);
   HRESULT GetDeactivateOnReturn([out]BOOL* pbDeactivate);
   HRESULT SetMyTransactionVote ([in]TransactionVote txVote);
   HRESULT GetMyTransactionVote ([out]TransactionVote* ptxVote);
}

IContextState is also discussed in Chapter 3, in the context of deactivating JITA objects. IContextState provides the method SetMyTransactionVote( ) used to set the transaction vote. You can pass SetMyTransactionVote( ) the enum value TxCommit, if you want to commit, or the enum value TxAbort, if you want to abort the transaction.

SetMyTransactionVote( ) returns CONTEXT_E_NOTRANSACTION when called by a nontransactional component.

Your object should vote to abort the transaction when it encounters an error that merits aborting the transaction. If all went well, your object should vote to commit the transaction. Example 4-1 shows a typical voting sequence. The object performs some work (the DoWork( ) method) and, according to its success or failure, votes to commit or abort the transaction. If your component decides to abort the transaction, it should return an error code indicating to its client that it aborted the transaction. The client can then decide to retry the transactional operation or handle the error some other way. This is why the component in Example 4-1 returns CONTEXT_E_ABORTING from the method after aborting the transaction. CONTEXT_E_ABORTING is the standard returned value from a component that aborted a transaction.

Example 4-1. Voting on the transaction’s outcome by accessing the IContextState interface and calling SetMyTransactionVote( )

STDMETHODIMP CMyComponent::MyMethod(  )
{
   HRESULT hres = S_OK;
   hres = DoWork(  );
   //Vote on the transaction outcome
   
   IContextState* pContextState = NULL;
   ::CoGetObjectContext(IID_IContextState,(void**)&pContextState);
   ASSERT(pContextState!= NULL);//Not a configured component
   
   if(FAILED(hres))
   { 
      hres = pContextState->SetMyTransactionVote(TxAbort);
      ASSERT(hres != CONTEXT_E_NOTRANSACTION);//No transaction support 
      hres = CONTEXT_E_ABORTING;
   }
   else
   {
      hres = pContextState->SetMyTransactionVote(TxCommit);
      ASSERT(hres != CONTEXT_E_NOTRANSACTION);//No transaction support
   
   }   
   pContextState->Release(  );
   return hres;
}

However, what should the client do if an inner object (not the root) votes to abort the transaction? The root object may not know that an inner component has aborted the transaction (and may still vote to commit and return S_OK to the client). If S_OK is allowed to return to the client, then the client never knows that its request was aborted. To prevent this situation, the interceptor between the root object and its client detects that the transaction is already doomed if an inner object votes to abort and the root object votes to commit and tries to return S_OK to the client; it returns CONTEXT_E_ABORTED to the client instead.

Tip

One interesting point regarding transaction termination involves exceptions. Any unhandled exception in any object in the transaction (not just the root) terminates the transaction immediately.

Get COM & .NET Component Services now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.