Page #94 (Transaction Theory)


COM+ Support for Transactions

At 25,000 feet, the support for transaction under COM+ is very straightforward. A transaction spans across many objects (that may access different databases). If an operation from any one object involved in the transaction fails, it indicates its failure status to COM+. The COM+ infrastructure can then roll back the changes to all the databases involved in the transaction.

A little clarification is in order. All the operations in a transaction need not always deal with databases, though a database operation is the most frequent use-case. Sending an e-mail or copying a file, for example, could also be considered as part of the transaction, and may require a rollback. Generally speaking, rollbacks are applied to any transactional resource, a database being just one such resource.

Two basic questions arise:

  • How does the COM+ infrastructure know if an object will participate in a transaction?

  • How does the object indicate, or vote, on the status of its operation?

Let s tackle the first question.

Configuring Transactions

COM+ defines an attribute on a component called the transaction attribute. By setting this attribute, a component tells COM+ to manage transactions on its behalf. When the component s object is activated, COM+ looks at the transaction attribute to determine the type of transaction protection it must provide in the object s context.

Why can t COM+ assume that a component will always participate in a transaction?

Forcing every object to participate in a transaction is not practical. The overhead of adding transaction protection to the context object is not acceptable for a component that has no interest in supporting a transaction.

The transaction attribute can be set from the Component Services snapin. The property-page for setting transactional attributes is shown in Figure 8.2.

Figure 8.2. Transactional settings on a component.

The transaction attribute can be set to one of the following values:

  • Required: This value implies that a component must have a transaction in order to do its work. If the component s object is activated within the context of an existing transaction, the transaction is propagated to the new object. If the activator s context has no transactional information, COM+ will create a brand new context containing transactional information and attach it to the object.

  • Required New: Sometimes an object may wish to initiate a new transaction, regardless of the transactional status of its activator. When the required-new value is specified, COM+ will initiate a new transaction that is distinct from the activator s transaction. The outcome of the new transaction has no effect on the outcome of the activator s transaction.

  • Supported: A component with this value set indicates that it does not care for the presence or absence of a transaction. If the activator is participating in a transaction, the object will propagate the transaction to any new object that it activates. The object itself may or may not participate in the transaction.

    This value is generally used when the component doesn t really need a transaction of its own but wants to be able to work with other components.

  • Not Supported: The component has no interest in participating in a transaction, regardless of the transactional status of its activator. This guarantees that the component s object will neither vote in its activator s transaction nor begin a transaction of its own, nor will it propagate the caller s transaction to any object that it activates. This value should be chosen if you wish to break the continuity of an existing transaction.

    Not supported is the default value for all components.

  • Disabled: If a component will never access a transactional resource, setting the transaction attribute to disabled eliminates any transaction-related overhead for the component. This attribute simulates the transaction behavior of a non-configured component.

The transaction attribute on a component can also be specified in the IDL file. The SDK defines the following constants. These constants are defined in the header file <mtxattr.h>.





The transaction attribute can be specified on the coclass entry in the IDL file, as shown here:

 import "oaidl.idl";  import "ocidl.idl";  #include <mtxattr.h>  ...  [   uuid(0AC21FA4-DB2A-474F-A501-F9C9A062A63E),    helpstring("AccountMgr Class"),    TRANSACTION_REQUIRED  ]  coclass AccountMgr  {   [default] interface IAccountMgr;  }; 

When the component is installed, the Catalog Manager (see Chapter 5) automatically configures the component with the value specified in the IDL file. However, the administrator can override this value from the Component Service snap-in at any time.

Now, let s get the answer to the second question how does an object cast its vote in a transaction?

Programmatic Voting

Once the components are set to participate in a transaction, each of the component s objects participating in the transaction has to indicate the outcome of its operation(s) individually.

Recall from Chapter 5 that, for a configured component, the state of the context object is available to the component s object via interface IContextState. This interface has a method on it called SetMyTransactionVote. Following is its prototype:

 HRESULT SetMyTransactionVote(TransactionVote txVote); 

Parameter txVote can be set to one of two possible values: TxAbort to indicate that an operation failed, and TxCommit to indicate that the operation succeeded.

Let s revise the account management code to use SetMyTransactionVote. The following code fragment shows the changes:

 STDMETHODIMP CAccountMgr::Debit(BSTR bsClient, long lAmount)  {   CComPtr<IContextState> spState;    HRESULT hr = ::CoGetObjectContext(__uuidof(IContextState),      (void**) &spState);    if (FAILED(hr)) {      return hr;    }    try {     ADOConnectionPtr spConn = OpenAccountsDB();      long lCurrentBalance = GetBalance(spConn, bsClient);      if (lCurrentBalance < lAmount) {       spState->SetMyTransactionVote(TxAbort);        return Error(_T("Not enough balance"), GUID_NULL,          E_FAIL);      }      long lNewBalance = lCurrentBalance - lAmount;      UpdateBalance(spConn, bsClient, lNewBalance);    }    catch(_com_error& e) {     spState->SetMyTransactionVote(TxAbort);      return Error(static_cast<LPCTSTR>(e.Description()),        GUID_NULL, e.Error());    }    spState->SetMyTransactionVote(TxCommit);    return S_OK;  } 

Similar changes need to be made to the stock manager and the trade manager components. The following code fragment shows the changes for the trade management component:

 STDMETHODIMP CTradeMgr::BuyStocks(BSTR bsClient, BSTR bsSymbol,    long lShares)  {   CComPtr<IContextState> spState;    HRESULT hr = ::CoGetObjectContext(__uuidof(IContextState),      (void**) &spState);    if (FAILED(hr)) {      return hr;  }    try {     //      // First operation - Obtain the stocks.      //      IStockMgrPtr spStockMgr(__uuidof(StockMgr));      long lAmount = spStockMgr->BuyStock(bsSymbol, lShares);      //      // Second operation - Debit the clien't account balance      //      IAccountMgrPtr spAccountMgr(__uuidof(AccountMgr));      spAccountMgr->Debit(bsClient, lAmount);    }catch(_com_error& e) {     spState->SetMyTransactionVote(TxAbort);      return Error(static_cast<LPCTSTR>(e.Description()),        GUID_NULL, e.Error());    }    spState->SetMyTransactionVote(TxCommit);    return S_OK;  } 

Let s run the simulation once again. Remember to set all the three components with the Required transactional attribute and to reset both the databases to the original values.

For your review, the VBScript code for the base client is shown below:

 set TradeMgr = CreateObject("TradeMgmt.TradeMgr")  TradeMgr.BuyStocks "Chris", "MSFT", 1000 

When the above code is executed, the transaction will fail, as Chris does not have enough funds to buy the stock. This result is the same as before. However, the difference will become apparent when you examine the StocksDB database. The number of shares for MSFT has not changed, unlike the earlier simulation where 1000 shares of MSFT just disappeared from the database.

COM+ provided automatic transaction support and rolled back the changes when the transaction failed.

This brings us to a new set of questions how did COM+ know the type of resource a component uses? The simulation program never informed COM+ that it used two MSDE databases. What if the simulation used a normal file as one of the resources? Each type of resource involved in a transaction requires its own specialized rollback. Surely, it is not possible for the COM+ infrastructure to have an intimate knowledge of rolling back changes for every possible resource type. How, then, can it still support transactions?

It s time to look at the architecture.


COM+ Programming. A Practical Guide Using Visual C++ and ATL
COM+ Programming. A Practical Guide Using Visual C++ and ATL
ISBN: 130886742
Year: 2000
Pages: 129 © 2008-2017.
If you may any questions please contact us: