Click here to Skip to main content
Click here to Skip to main content
Go to top

Using TIP (Transaction Internet Protocol) to Enlist Web Services into COM+ Distributed Transactions

, 4 Apr 2006
Rate this:
Please Sign up or sign in to vote.
Shows how to use TIP to communicate with web services in a COM+ distributed transaction.

Introduction

COM+ distributed transactions can combine several disparate data sources under one transaction with a two-stage commit. Code running on different machines or in different processes can also participate in a distributed transaction using a TIP (Transaction Internet Protocol) URL.

Notice

TIP, while still available in .NET 1.1 and .NET 2.0, is an aging technology. With the upcoming release of Windows Vista, Microsoft will be phasing out its support for TIP. If you are still interested in transactional web services, there is an alternative. Check out my new article on Vista's WS-AtomicTransaction here[^]. As more companies adopt the WS-AtomicTransaction standard, this will be the way to do distributed transactions across web services in the future.

Background

Distributed transactions are my favorite part of using COM+. By opening up a distributed transaction, I can get a bunch of different databases, message queues, files, email, and almost anything else I can think of into the same transaction. The two-stage commit tells me that they all work or they all abort. And the transaction can be restarted from where it left off if a server goes down.

TIP is actually a well-documented, standardized protocol. It allows several applications to communicate information about a transaction. With a normal distributed transaction, the DTC (Distributed Transaction Coordinator) communicates with your data sources to control the two-stage commit. Other applications on the same machine could potentially enlist in that transaction as long as the transaction object is passed to them. But, an application running on a different machine needs something better. This is what TIP is for.

This article borrows a lot from my previous article. In that article, I used NHibernate and got it to enlist in a COM+ distributed transaction. This article also needs to do some data access as an example, so I decided to just copy the NHibernate stuff over here. You do not have to use NHibernate to use the technique described in this article.

Using the code

There are several things I do in my code that you should be aware of:

  • There is a web services project contained in the code called ComPlusTipWebService. Be sure to mount this as a virtual directory with the same name in IIS before opening up the solution.
  • The COM+ project has a post-build on it. The post-build uses regsvcs to unregister and re-register the COM+ DLL. This adds a little waiting time to compilation but saves me the trouble of having to do it by hand. To make regsvcs work, I added the framework directory to my path (C:\Windows\Microsoft.NET\Framework\v1.1.4322). As always, if you change your path variable, you have to restart Visual Studio.
  • I have created an NUnit test harness project as well to run the actual tests.
  • In the post-build for the test harness project, I copy the App.Config to the target directory and give it the DLL name with ".config" appended to it. This is so NUnit can pick it up.
  • Usually, I set up my test harness projects to be the startup projects. To make NUnit start the project, bring up the project properties window, select Configuration Properties->Debugging from the left side. Set the Debug Mode to Program, and hit Apply. For the start application, find the NUnit GUI executable (C:\Program Files\NUnit 2.2\bin\nunit-gui.exe). For the command line arguments, put the filename of the DLL (CPNTestHarness.dll).
  • I have included a SQL script to create the tables that I use in the example. Put these tables into your database and change the connection string located in the Web.Config for the web services project.
  • You may have noticed the weird way that I set up the NHibernate configuration. Instead of using the App.Config to set it up, I do it with code and pull the connection string from the App.Config. This is just a personal preference. I generally find that, in enterprise environments an App.Config just won't do the job, and usually pull my configuration settings from the Microsoft enterprise libraries. So I just quickly changed the code to work with this example.

To get transactions working from client side to server side, there are a number of changes that must be made to the computers running it. In order to transmit DTC transaction information, a TIP URL is used. TIP is enabled by default on Windows 2000 but disabled by default on Windows XP and Server 2003. Also, it looks like Windows XP has a service pack that disables the network DTC access in general. To turn this back on, use the EnableTip.reg registry file to fix the keys. What it's looking at is the following:

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MSDTC\Security

The following keys are set to 1:

  • NetworkDtcAccess
  • NetworkDtcAccessTip
  • NetworkDtcAccessTransactions

After setting these keys, restart the DTC service by opening up a command prompt and typing the following commands:

net stop msdtc
net start msdtc

ITipTransaction interface

After starting a transaction, the ContextUtil class gives you access to a Transaction property. The value of this property is an object that describes the current COM+ transaction. What you can do is cast this object to a System.EnterpriseServices.ITransaction interface to get a little information out of it. Usually, this is not necessary though, since the EnterpriseServices library is taking care of most of the work for you.

In addition to being able to cast the transaction object to ITransaction, you can also cast it to another interface called ITipTransaction. This interface does not exist in the System.EnterpriseServices library though. Which means you have to create one yourself to match the COM interface. Here is the code:

using System;
using System.Runtime.InteropServices;

namespace Common
{
    /// <summary>
    /// An interface that matches the COM+ ITIPTransaction
    /// interface. We can use this to pull out the
    /// TIP URL and send it to the server.
    /// </summary>
    [ComImport]
    [Guid("17CF72D0-BAC5-11d1-B1BF-00C04FC2F3EF")]
    [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    public interface ITipTransaction 
    {
        /// <summary>
        /// Push
        /// </summary>
        /// <param name="i_pszRemoteTmUrl"></param>
        /// <param name="o_ppszRemoteTxUrl"></param>
        void Push([MarshalAs(UnmanagedType.LPStr)] 
             string i_pszRemoteTmUrl,
             [MarshalAs(UnmanagedType.LPStr)] out string
             o_ppszRemoteTxUrl);

        /// <summary>
        /// Gets the transaction URL.
        /// </summary>
        /// <param name="o_ppzLocalTxUrl"></param>
        void GetTransactionUrl([MarshalAs(UnmanagedType.LPStr)] out string
            o_ppzLocalTxUrl);
    }
}

The interface is pretty simple. The method we're interested in the most is GetTransactionUrl since the TIP URL is really all that we need. With our interface created, all we have to do to get the TIP URL for a currently open transaction is this:

ITipTransaction trans = (ITipTransaction)ContextUtil.Transaction;
string tipUrl = "";
trans.GetTransactionUrl(out tipUrl);
return tipUrl;

That's it! Now that we have the TIP URL, we can pass that to other applications so that they can enlist in the distributed transaction. The process for enlisting is also just as simple. Let's say I have a ServicedComponent that looks like this:

[Transaction(TransactionOption.Supported)]
public class OrmManager : ServicedComponent { ... }

This class talks to my data source. I can create an object of this type within the TIP transaction's context. To do this, I use the System.EnterpriseServices.BYOT (Bring Your Own Transaction) class:

OrmManager om = null;
om = BYOT.CreateWithTipTransaction(tipUrl, 
        typeof(OrmManager)) as OrmManager;

How the code works

This code is actually an extension of my previous article. So, if there are some confusing aspects about how I use NHibernate or the test harness, please take a look at that article. Basically, I had a TransactionController class in that code that allowed me to do Save, Delete, and GetAll on a particular NHibernate class. I only implemented the methods I needed to perform the tests. The NUnit test harness from the previous article ran two tests to make sure that the distributed transactions were working. This article's code is not too much different. The big difference being that the persistence is done behind a web-service. So it's completely out of the application domain of the test harness. I did this to drive home the point that using TIP in an SOA environment could have some very interesting results.

Summary

I do thoroughly enjoy transactional code. Getting the databases, message queues, mainframe, and all the third party software to communicate under the same transaction would be quite a thrill. Especially in the case of a service oriented architecture. However, I don't see many people using COM+ to do this. I imagine there are many reasons, the biggest being how slow it is to get DTC to coordinate everything. Instead, it seems that a lot of programmers will choose to write some kind of proprietary transaction handling or just forget it all together. I hope that my articles can hope to uncover just how powerful and easy COM+ really is.

History

  • 1.0: 2006-02-08: Initial revision.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

Share

About the Author

Dustin Metzgar
Software Developer
United States United States
No Biography provided

Comments and Discussions

 
GeneralFollowup PinmemberJack Loomis20-Feb-06 6:25 
AnswerRe: Followup PinmemberDustin Metzgar24-Mar-06 3:36 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web02 | 2.8.140916.1 | Last Updated 4 Apr 2006
Article Copyright 2006 by Dustin Metzgar
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid