16.6 Automatic Transactions

Microsoft Transaction Server (MTS), COM+ 1.0, and the .NET CLR support the same automatic distributed transaction model. The .NET Framework provides support for transactional components through COM+ services. There are two key benefits to COM+ transactions:

  • They allow distributed transactions that span multiple data sources.

  • Objects that can participate in COM+ transactions are free from having to anticipate how they might be used within a transaction. A client can perform different tasks with multiple objects, all in the context of a single transaction without the participating objects being aware of the transaction.

Instances of a .NET Framework class can participate in automatic transactions. Once an object is marked to participate in a transaction, it automatically executes within a transaction. This transactional behavior is controlled by the value of the transaction attribute for the .NET class, ASP.NET page, or XML web service method using the object. This allows the instantiated object to be configured programmatically to participate automatically in an existing transaction, to start a new transaction, or to not participate in a transaction.

When a transactional object accesses a data resource, a transaction occurs according to the value of the declarative transaction attribute of the object. When the transactional object accesses a data resource, the data driver enlists in the transaction through the distributed transaction coordinator (DTC).

During the lifetime of an automatic transaction, the objects participating in it can vote to either commit the transaction they are participating in by calling the static SetComplete( ) method of the ContextUtil class or to abort the transaction by calling the static SetAbort( ) method of the ContextUtil class. In the absence of an explicit vote, the default is to commit the transaction. The transaction is committed once it completes if none of the participating objects have voted to abort.

Alternatively, you can apply the AutoCompleteAttribute attribute to a transactional method. This attribute instructs .NET to automatically commit the transaction, provided no exceptions are encountered. If an unhandled exception is thrown, the transaction is automatically rolled back.

For a .NET Framework object to participate in an automatic transaction, it must be registered with COM+ component services. The following steps will prepare a class to participate in an automatic transaction:

  1. Apply the TransactionAttribute to the class, and specify the transaction behavior, timeout, and isolation level.

  2. Derive the class from ServicedComponent allowing access to COM+ services.

  3. Sign the assembly with a strong name. Use the sn.exe utility to create a key pair with the following syntax:

    sn -k MyApp.snk

    Add the AssemblyKeyFileAttribute or AssemblyKeyNameAttribute assembly attribute specifying the file containing the key pair, for example:

    [assembly: AssemblyKeyFileAttribute("MyApp.snk")]
  4. Register the assembly containing the class with the COM+ catalog[1] by executing the .NET Services Registration Tool (regsvcs.exe) with the following syntax:

    [1] COM+ services are explored in detail in COM and .NET Component Services, by Juval Löwy (O'Reilly).

    regsvcs /appname:MyApp MyAssembly.dll

    This step isn't strictly required. If a client calling the class is managed by the CLR, the registration is performed automatically.

This example is a simple .NET class that participates automatically in transactions:

using System.EnterpriseServices;
using System.Runtime.CompilerServices;
using System.Reflection;

namespace ServicedComponentCS
{
    [Transaction(TransactionOption.Required)]
    public class ServicedComponentTest : ServicedComponent
    {
        [AutoComplete]
        public void TranTest()
        {
            bool success = true;

            // ... do work and store the outcome to success variable

            if(success)
            {
                // don't need the next line since AutoComplete
                // ContextUtil.SetComplete();
            }
            else
            {
                ContextUtil.SetAbort();
                throw new System.Exception("Error in Serviced " +
                    "Component. Transaction aborted.");
            }
        }
    }
}

The TransactionOption.Required attribute in this example indicates that every method in this class must be used in a transaction, but the method can use the existing transaction if one has already been started in the caller's context. The full list of TransactionOption values is featured in Table 16-4.

Table 16-4. Values for the TransactionOption enumeration

Value

Description

Disabled

Indicates that transactions aren't created or used automatically with this object.

NotSupported

Indicates that the object doesn't run within the scope of transactions. When a request is processed, its object context is created without a transaction, regardless of whether there is a transaction active.

Supported

Indicates that the object runs in the context of an existing transaction, if one exists. If no transaction exists, the object runs without a transaction.

Required

Indicates that the object requires a transaction. It runs in the scope of an existing transaction, if one exists. If no transaction exists, the object starts one.

RequiresNew

Indicates that the object requires a transaction, and a new transaction is started for each request.



    Part I: ADO.NET Tutorial
    Part II: ADO.NET Core Classes
    Part III: API Quick Reference