Understanding COM+ with VFP, Part 3 

This article may contain URLs that were valid when originally published, but now link to sites or pages that no longer exist. To maintain the flow of the article, we've left these URLs in the text, but disabled the links.

Understanding COM+ with VFP, Part 3

Craig Berntson

Transactions are an important part of any data update mechanism. Part 3 of this series by Craig Berntson introduces the Distributed Transaction Coordinator and explains how to use transactions under COM+.

Transactions, transactions, transactions. Without them, we can't be sure that data is getting written to all of the Tables involved in an update. Before digging into how transactions work in COM+, let's do a quick review of transactions.

A review of transactions

Joe wants to transfer $100 from his savings account to his checking account. He walks up to the ATM, inserts his card, and presses the buttons to initiate the transfer. Behind the scenes, this transfer can be accomplished two ways. The first way is that the balance of Joe's savings account can be reduced by $100 and then his checking account balance increased by $100. The second option is that his checking account balance can be increased and then his savings account balance decreased. But what happens if there's a system crash between the two updates? Under the first scenario, Joe isn't happy. He's lost $100. Under the second example, Joe is very happy. He's $100 richer, but the bank has now lost $100. What we want is that, in the event of the aforementioned crash, either both accounts must be updated or neither of the accounts updated. To ensure that this happens is the purpose of transactions.

Transactions should follow the ACID rule—that is Atomicity, Consistency, Isolation, and Durability. Atomicity means that either all or none of the update is committed. Consistency means that if the transaction fails, the data is returned to the same state as before the transaction started. Isolation means that one transaction doesn't know what another transaction is doing. Finally, Durability means that the transaction state is kept, no matter what happens to the system. This is generally handled through the use of a transaction log.

We can implement transactions on our VFP data by using the BEGIN TRANSACTION/END TRANSACTION/ROLLBACK commands. This pseudo-code shows the preceding example:

  BEGIN TRANSACTION
llSavingsOk = UpdateSavings(-100)
llCheckingOk = UpdateChecking(100)
IF llSavings AND llChecking
    END TRANSACTION
ELSE
    ROLLBACK
ENDIF

The transactions in VFP only work with VFP data, and VFP transactions fail the Durability rule of ACID. There's no transaction logging.

If we're using SQL Server, we need to use a different mechanism. We can use ADO commands to tell SQL Server when to begin and end a transaction or we can code it in a stored procedure. However, what happens if the savings account is in SQL Server and the checking account data is in Oracle? SQL Server transactions will only work on SQL Server, and Oracle transactions will only work on Oracle. We need a single transaction that will work against both databases. That's where the Distributed Transaction Coordinator (DTC) comes in.

Using the DTC

The DTC allows us to have transactions that cross databases. This means that we can have data in both SQL Server and Oracle that will be updated by the same transaction. Under MTS and VFP 6, Fox data couldn't participate in a DTS transaction. (This changes under COM+ and VFP7. More on that later.) Back to the DTC.

The DTS uses a two-phase commit. Basically, in phase one, DTS asks the database if it can do an update. If all of the databases respond yes to this question, then phase two begins, in which DTS tells the database to update the data. If any of the databases respond with a no, then DTS tells all of the databases to roll back the update.

Let's look at how transactions were handled under MTS:

  DEFINE CLASS Math AS SESSION OLEPUBLIC
   FUNCTION Multiple(tnNum1, tnNum2)
      LOCAL lnResult, loMtx, loContext

      * Create a reference to the MTS object
      loMtx = CREATEOBJECT("MTXAS.APPSERVER.1")

      * Create a reference to the context object
      loContext = loMtx.GetObjectContext()

      lnResult = tnNum1 * tnNum2

      * Commit the transaction if there is one and
      * tell MTS that we're done using the component
      loContext.SetComplete()

      * We can also abort the transaction and 
      * tell MTS that we're done with the component
      * loContext.SetAbort()

      RETURN lnResult
   ENDFUNC
ENDDEFINE

Note that when we either commit or abort the transaction, MTS will also release the component. This means that if the very next command in our application needs the component, we have to do another CREATEOBJECT() on the client. Let's see how this changes in COM+:

  DEFINE CLASS Math AS SESSION OLEPUBLIC
 FUNCTION Multiply(tnNum1 AS Number, tnNum2 AS Number) ;
   AS Number HELPSTRING "Multiplies ;
      two numbers and returns the result"
    LOCAL lnResult, loMtx, loContext, loContextState, ;
          llDone, llGetDone, lnTxnState, lnGetTxnState

    llDone = .T.
    llGetDone = .F.
    lnTxnState = 1
    lnGetTxnState = 0

    * Create a reference to the MTS object
    loMtx = CREATEOBJECT("MTXAS.APPSERVER.1")

    * Create a reference to the Context object
    loContext = loMtx.GetObjectContext()

    * Get an interface to the Context state
    loContextState = GETINTERFACE(loContext, ;
       "iContextState")
    lnResult = tnNum1 * tnNum2

    * Handle Transaction Setting (Consistency)
    * 0 = commit, 1 = abort
    loContextState.SetMyTransactionVote(lnTxnState)
    loContextState.GetMyTransactionVote(@lnGetTxnState)

    * Handle activation setting (Done)
    * .T. = Deactivate, .F. = Leave activated
    loContextState.SetDeactivateOnReturn(llDone)
    loContextState.GetDeactivateOnReturn(@llGetDone)
    RETURN lnResult
  ENDFUNC
ENDDEFINE

In this example, we can either commit or abort the transaction with SetMyTransactionVote, but still keep the instance of the component active until we explicitly call SetDeactivateOnReturn with a .T. parameter.

You might now be wondering where the DTC fits in. COM+ automatically calls the DTC for us. The DTC talks to the database through the use of a Resource Manager. MTS and COM+ ship with Resource Managers for Oracle and SQL Server, but not for VFP. That's why VFP data couldn't take part in MTS transactions.

It turns out that Resource Managers are very difficult to implement. So, COM+ has what are called Compensating Resource Managers (CRMs). CRMs are easier to implement than a Resource Manager. By using a CRM we can have our Fox data be involved in DTC transactions.

A CRM consists of two parts, the CRM Worker and the CRM Compensator. These correspond to the two-phased commit of the DTC. A detailed discussion of the CRM is beyond the scope of this article. However, VFP 7 ships with a CRM sample application. You'll find it in the SAMPLES\COM+\CRM folder under your VFP 7 installation.

Configuring COM+ transactions

Like many of the features in COM+, transactions are managed at runtime. Open the Component Services Manager applet and drill down to one of your components. Right-click on the component and select Properties, and then select the Transactions tab. You'll see five transaction settings (see Figure 1 and Table 1).

Table 1. Transaction types supported for COM+ components.

Setting Description
Disabled Transactions aren't needed. Set this when you don't want the extra overhead of a transaction—for example, a component that doesn't update any data.
Not Supported Prevents a component from using a transaction, regardless of the transactional state of the calling component.
Supported The component participates in the transaction if one is active.
Required The component participates in the transaction if one is active. If there's no active transaction, a new one is started.
Requires New A new transaction is always started.

You'll also notice the "Override global transaction timeout value" check box. When you select Required or Requires New transactions, this check box becomes enabled. You can then enter the number of seconds the transaction will run before timing out.

That's it for transactions. In Part 4, we'll look at a COM+ feature that gives you asynchronous calls: Queued Components.

To find out more about FoxTalk and Pinnacle Publishing, visit their website at http://www.pinpub.com/html/main.isx?sub=57

Note: This is not a Microsoft Corporation website. Microsoft is not responsible for its content.

This article is reproduced from the July 2001 issue of FoxTalk. Copyright 2001, by Pinnacle Publishing, Inc., unless otherwise noted. All rights are reserved. FoxTalk is an independently produced publication of Pinnacle Publishing, Inc. No part of this article may be used or reproduced in any fashion (except in brief quotations used in critical articles and reviews) without prior consent of Pinnacle Publishing, Inc. To contact Pinnacle Publishing, Inc., please call 1-800-493-4867 x4209.

© Microsoft Corporation. All rights reserved.