ObjectContext Object |
As of version 2.0, an important feature of Active Server Pages is the ability to create a transactional script: one whose constituent code segments all succeed completely or fail as a group. For example, using such a script, one section of code could remove a record from an inventory table, and a second section could add a record to a sales log table. However, only if both sections of code succeed does the script itself succeed. If the removal of the inventory record or the addition of the sales record fails, the script itself fails. Both processes are rolled back: the deleted record, if it was removed, is added back into the database, and the sales record, if it was added, is removed from the sales log table. This ability to wrap several functions in a single transactional unit that succeeds or fails as a whole is an important improvement in the power of ASP applications. Previously, all transactions relied on database transaction support.

ASP application transactions are controlled by Windows 2000 COM+ Component Services or Windows NT's Microsoft Transaction Server (MTS). This piece of the BackOffice suite allows control over all database actions coded to use it. Support for transactional scripts is built into IIS and Personal Web Server and does not require any special setup. Without COM+ Component Services or, in ASP 2.0, MTS transactional support, your applications would have to track all database changes manually and roll back all database actions by hand, keeping track of multiuser and concurrency issues, etc. MTS or COM+ Component Services gives this support for very little extra coding—as long as the database your application is connected to is Microsoft SQL Server or it supports the XA protocol from the X/Open consortium. Note that this means that file actions are not yet supported—or at least, not automatically.

ASP's support of transactions is coded through the use of the ObjectContext object, which represents the actual ObjectContext object of COM+ Component Services itself. By calling methods of the ObjectContext object and coding its events, you can create a transactional script with only a few more lines of code.

To declare all the script on a given page to be transactional, simply add the following line of code as the first line in your script:

    <%@ TRANSACTION = Required %>

For more details on the TRANSACTION ASP directive, see Chapter 11. Here it is important only that this line be the first in your script; including this line alerts the web server to use Component Services to ensure that the script succeeds or fails as a whole.

To commit the transaction or abort it, you simply call the SetComplete or SetAbort methods of the ObjectContext object, respectively. If you are dealing with a complex transaction containing segments of code that are not supported by Component Services (notably file actions), you can specially code for these actions in the ObjectContext events OnTransactionCommit and OnTransactionAbort. There are examples of all of these methods and event procedures in the reference section later in this chapter.

Comments/Troubleshooting |
There are currently two very important limitations in constructing transactional scripts:

  Only database actions are supported, and only SQL Server and databases that support the XA protocol are supported by COM+ Component Services or MTS.

  A transaction cannot span more than one ASP page. For this reason, you must be very careful in creating your pages: they must include all the actions required by your transactions but not be so large as to slow the processing of the page by too large a percentage.

If you write your own server components that complete some or all of the database actions in your transaction, that component must be registered in an MTS package. (MTS transactional support is provided only if the component is registered. What's more, you should create your own library packages and not include your component in the IIS in-process package. Custom library packages can be used by multiple ASP applications and are run in the same process as the ASP DLL. Setting up library packages also gives your component the ability to be pooled for reuse by your applications. This pooling is managed by MTS as well. You also can add your components to a server package, but doing so is required only for role-based transactions or transactions running on remote computers.)

For more information on the ObjectContext object and server components, see Developing ASP Components, written by Shelley Powers and published by O'Reilly & Associates.

Note that you should not give objects functioning in transactions session- or application-level scope, since transactional objects are deactivated at the end of their transaction. If you do give such an object session or application scope, calls after the end of the transaction will fail and raise an error.

Although transactions are supported only for database actions, you can add code to the OnTransactionCommit and OnTransactionAbort event procedures to provide your own nondatabase transactional support. For example, code in these event procedures could easily be used to write or remove files from the file system upon success or failure of a given transaction.

ObjectContext exposes six methods other than the ones you can access through ASP. However, these are accessible only through code within the server components being managed by COM+ Component Services or MTS, and therefore are not documented here.

Transactional scripts are a very important addition to ASP. If you had access to database transactions only through use of ActiveX Data Objects, it would still be a very important and useful function. However, by creating custom server components, you can create complex and powerful transactions.

SetAbort |
ObjectContext.SetAbort
Aborts the transaction as a whole. When it is called, the transaction ends unsuccessfully, regardless of code that has or has not already been processed in your script. You can use this method in your script after testing for the completion of a given part of the transaction, or a server component managed by MTS or COM+ Component Services can call it. Calling SetAbort rolls back any parts of the transaction that have already occurred and calls the ObjectContext_OnTransactionAbort event procedure if one exists in your script.
Parameters
None
Example
    <%

    ' The following code tests the result from a method call
    ' to a custom server component that attempts to remove
    ' a book from the inventory table and then tests the
    ' results from a credit card check.

    ' Based on this code and the segment that follows it, the
    ' script will call either the SetAbort or the SetComplete
    ' method of the ObjectContext object.

    ' Attempt to sell 2 copies of the book Animal Farm.
    intBooks = MyInventory.SellBook("Animal Farm", 2)

    ' Check the credit card given by the client.
    intCheckCC = MyCreditChecker.ChkCard("0001231234")

    If intBooks = 2 And intCheckCC = 0 Then

       ' Complete the transaction. Two copies of the book
       ' are in the inventory and the credit card checks out.
       ObjectContext.SetComplete

    Else

       ' Abort the transaction. Either there are not two
       ' copies of the book in the inventory or the credit
       ' card did not check out.
       ObjectContext.SetAbort

    End If

    %>
Notes
Any segment of a transactional script can call the SetAbort method. Note that if you have code that exists after the call to SetAbort, it will not be processed until after the execution of the OnTransactionAbort event procedure, if one exists. For this reason, be sure that your OnTransactionAbort event procedure performs any cleanup that is necessary for actions that are not supported in a transaction (notably file actions).

If you want some code to be processed regardless of a call to SetAbort, make sure that it is before the call to SetAbort in the script, or test for completion of the transaction after your code in the script.

SetComplete |
ObjectContext.SetComplete
Signals the successful completion of a transaction. When it is called, the code in the OnTransactionCommit event procedure code is processed if it exists. A call to the SetComplete method from within the script itself only indicates the success of the script on the page. It does not override possible failure of the code within the components referenced in the script. All transactional components in the script must signal SetComplete for the transaction to commit.
Parameters
None
Example
See the example in the previous section, "SetAbort."
Notes
Note that calling SetComplete does not necessarily mean that the entire transaction is complete. Every component called from the script also must call the SetComplete method of the ObjectContext object. If you do not explicitly call SetComplete, the transaction is complete only after all code is processed without any calls to SetAbort. If no call to SetAbort is made by the end of the script, the OnTransactionCommit event procedure code is processed if it exists, regardless of whether SetComplete is called.

OnTransactionAbort |
OnTransactionAbort( )
The OnTransactionAbort event procedure is processed immediately if the SetAbort method of the ObjectContext object is called explicitly in scripted code or by a server component called from the scripted code. If no code calls the SetAbort method, this event procedure is never processed.
Parameters
None
Example
    <%

    ' The following code procedure is processed when the code in
    ' the SetAbort method example is processed.
    Sub OnTransactionAbort ( )
    %>
       Your book sales transaction could not be completed.
       Either there was not sufficient inventory for your
       sale to be processed, or your credit card did not
       go through.
    <%
       ' Clean up any nontransactional actions here...

    End Sub

    %>
Notes
Use OnTransactionAbort to clean up any nonsupported actions your transaction makes that must be reversed if the transaction fails. This includes changes to variables (session- and application-level scope), the registry, and the file system. Note, however, that your server components should clean up after themselves.

You also should use the OnTransactionAbort event to inform the client that the transaction has failed.

Do not call the SetAbort or SetCommit methods from the OnTransactionAbort event procedure. Doing so may introduce a loop and result in the loss of function for your application and/or a loss of data.

OnTransactionCommit |
OnTransactionCommit( )
The OnTransactionCommit event procedure is processed immediately if the SetComplete method of the ObjectContext object is called explicitly in scripted code or by a server component called from the scripted code. It also is called implicitly if no script on the current page called the SetAbort method.
Parameters
None
Example
    <%

    ' The following code procedure is processed when the code in
    ' the SetComplete method example is processed.
    Sub OnTransactionCommit ( )
    %>
       Your book sales transaction was completed.
       Thank you for your sale.

    <%
       Session("intTotalSales") = Session("intTotalSales") + 1

       ' Process any nontransactional code here...

    End Sub

    %>
Notes
The OnTransactionCommit event procedure can be used to inform the client of the success of the transaction. It also can be used for code that you want to be processed only if the transaction completes successfully. Do not call the SetAbort or SetCommit methods from the OnTransactionCommit event procedure. Doing so may introduce a loop and result in the loss of function for your application and/or a loss of data.