Develop Transactional .NET Web Services

With distributed applications becoming pervasive, you need tools and techniques to create, destroy, and monitor transactions across multiple platforms and servers. Having these tools and techniques available within a single architecture such as the .NET Framework makes it easy to develop distributed applications and handle transactions. This article looks at the transaction support the .NET Framework provides, which you can use to develop transactional .NET Web services.

Introduction to Transactions

Transaction processing is easy as long as the transaction involves only a single database. Unfortunately, a company rarely stores its data in a single database. In many cases, companies spread the information out over many databases. For example, an online retailer might store customer account information in a SQL Server database at the corporate office. But, it may store order-fulfillment information in an Oracle database at a partner company’s warehouse. When a customer places an order, the online retailer should debit the customer’s account in the SQL Server database at the corporate office and write an order record to the partner company’s Oracle database. So in this case, the retailer needs to perform the updates on both of these databases as a single transaction. If either operation fails, each resource manager will have to roll back its part of the transaction. This type of transaction is called a distributed transaction.

Considering the number of entities that may be involved in a distributed transaction, it may get really complex and require a lot of code for its support. After seeing the amount of time developers spent writing this type of plumbing code, Microsoft decided to build distributed transaction capabilities into the .NET Web services API. As its name suggests, this API provides for most of the plumbing code required to automate distributed transactions across .NET Web services.

The .NET Framework encapsulates most of the plumbing required for creating distributed, transaction-based Web services in easy-to-use, very flexible classes. These classes are contained in the System.EnterpriseServices namespace, and they rely on the services COM+ provides for supporting automatic transactions.

If you have done any MTS/COM+ programming using Visual Studio 5, you know that for a COM component to use COM+ services, it needs to have a reference to the ObjectContext interface that encapsulates most of the core COM+ functionality. The .NET framework has a class named ContextUtil that acts as a wrapper around the ObjectContext class. It also is contained in the System.EnterpriseServices namespace. The methods in the ContextUtil class are the primary means by which applications written in managed code communicate with Microsoft Distributed Transaction Coordinator (MSDTC).

One of the most important features the ContextUtil class provides is Transaction Processing Monitoring (TPM). The MSDTC internally maintains two flags through COM+, done bit and consistency bit, which you use not only to vote for the outcome of a transaction but also to control the deactivation of objects. Use the following methods to explore the combinations of done and consistency bits and indicate to the COM+ runtime the state of the transaction and whether the object can be deactivated:

  • SetComplete: This method sets both done and consistency flags to true, which indicates that the current transaction can be committed and the object deactivated.
  • SetAbort: This method sets the done bit to true and consistency flag to false, which indicates that the current transaction has to be rolled back, and the object deactivated.
  • EnableCommit: This method sets the done bit to false and consistency flag to true.
  • DisableCommit: This method sets both done and consistency flags to false.

The above-mentioned methods allow you to control both the transactional and the activation behavior of an object. However, you sometimes may want to individually manipulate the done bit and consistency bit for a finer level of control over the transactions. The ContextUtil class exposes the following two properties through which you can deal with the done bit and consistency bits individually:

  • MyTransactionVote: a property get and set through which you can either get or set the value of the consistency bit
  • DeactivateOnReturn: a property get and set that you use to either get or set the value of the done bit

You can programmatically specify whether you want to commit or rollback the transaction in a Web service in the following two ways:

  • Using the utility methods (explained above) that the ContextUtil class supplies
  • Using the AutoComplete attribute class

You will see both of these methods in action later.

This article demonstrates how to take advantage of the built-in transaction capabilities that .NET Web services provides when creating managed applications in the .NET Framework. It is sectioned into two parts:

  • The first part shows you how to create two transactional Web service methods. Within these methods, you will use the ContextUtil class to control transactions.
  • The second part shows you how to create Web service clients that will access your transactional Web services. While creating this Web service client, you also will learn how a transaction initiated by a Web service or a Web page can be used to control the transactional behavior of the consumed Web services.

Implementation of a Web Service

This demonstration begins by stepping you through the creation of two tables named Emp and Dept in a SQL Server database. You then will programmatically add records to these two tables by using EmployeeService and DeptService Web service classes. To start, create a new project named WebServiceTransactions, as shown in Figure 1.

Figure 1. Create a New Project

After you’ve created the project, rename the default Web service from Service1 to DeptService. The following section walks you through the code required to implement the DeptService Web service.

Implementation of DeptService Web Service

As mentioned previously, the DeptService class encapsulates the functionalities related to the Dept table, such as adding a new dept, updating an existing dept, and so on. This article considers only the addition of a new dept. The code for adding a new dept to the Dept table is as follows:

[WebMethod(false,TransactionOption.Required)]
public int AddDept(string deptName, string location)
{
   try
   {
      string connString =
         System.Configuration.ConfigurationSettings.AppSettings
         ["connectionString"];
      int deptNo;
      //Create the connectionection object passing to it the
      //connection string
      SqlConnection connection = new SqlConnection(connString);
      connection.Open();
      //Create and set the SqlCommand object
      SqlCommand command = new SqlCommand("AddDept",connection);
      command.CommandType = CommandType.StoredProcedure;

      //Add the DeptName parameter
      SqlParameter paramDeptName = new
         SqlParameter("@DeptName",SqlDbType.VarChar,50);
      paramDeptName.Value = deptName;
      paramDeptName.Direction = ParameterDirection.Input;
      command.Parameters.Add(paramDeptName);

      //Add the Location parameter
      SqlParameter paramLocation = new
         SqlParameter("@Location",SqlDbType.VarChar,50);
      paramLocation.Value = location;
      paramLocation.Direction = ParameterDirection.Input;
      command.Parameters.Add(paramLocation);

      //Add the DeptNo parameter as the Output parameter
      SqlParameter paramDeptNo = new
         SqlParameter("@DeptNo",SqlDbType.Int,4);
      paramDeptNo.Direction = ParameterDirection.Output;
      command.Parameters.Add(paramDeptNo);

      command.ExecuteNonQuery();

      deptNo = (int)command.Parameters["@DeptNo"].Value;

      ContextUtil.SetComplete();
      return deptNo;

   }
   catch(Exception ex)
   {
      ContextUtil.SetAbort();
      throw ex;
   }
}

Because you want to expose the AddDept method as a Web service, you decorate that method with the WebMethod attribute. To the constructor of the WebMethod class, you supply two parameters. The first parameter indicates that you want to disable the session for that method, and the second parameter specifies that the AddDept method always be executed as part of a transaction. Due to the transaction attribute Required, your AddDept method will run within the scope of the caller’s transaction (if the caller of the method is already running in a transaction). Otherwise, the AddDept method will create a new transaction for itself and run within the scope of that transaction:

[WebMethod(false,TransactionOption.Required)]

The possible values you can specify for the TransactionOption enum are:

  • Supported: Object will share the transaction of the client (if the client has one)
  • NotSupported: Object is created in a context without any governing transaction
  • Required: Shares the transaction with the client (if the client has one); otherwise, creates a new one for itself
  • RequiresNew: Creates the object in a new transaction context regardless of the caller’s current transaction context state
  • Disabled: Completely ignores any transaction in the current context

More by Author

Must Read