Together At Last: Microsoft Transaction Server and Visual Basic 6.0

August 1998

by Steve Hoag

As a Microsoft® Visual Basic® programmer, you've probably heard mention of Microsoft Transaction Server (formerly code-named Viper) over the past year or so. While it has always been possible to create Microsoft Transaction Server (MTS) components with Visual Basic, it hasn't always been easy. The release of Visual Basic version 6.0 and Microsoft Windows NT® version 4.0 Service Pack 4 changes that in a big way, making it easier than ever to create and debug MTS components in Visual Basic.

If you haven't had time to check out this relatively new technology, you may be asking yourself what is MTS, why should I use it, and how might I use it? This article approaches those questions from the perspective of the Visual Basic programmer, providing a high-level introduction to the integration of Microsoft Transaction Server with Visual Basic.

What Is Microsoft Transaction Server?

To make a short story long, according to marketing literature MTS is "a component-based transaction processing system for developing, deploying, and managing high-performing, scalable, and robust enterprise, Internet, and intranet server applications." If that sounds confusing, don't worry: the short version is that MTS makes developing distributed applications easy.

Microsoft Transaction Server is perhaps a poor choice for a name—it doesn't really describe what MTS is. Yes, it is from Microsoft, it does manage transactions, and it does act as a server—but it also does a whole lot more. It's also an object manager, a thread pool manager, a security manager, and a key piece of Microsoft's distributed application framework. You might think of it as a "black box" that can replace as much as 40 percent of the infrastructure code needed to create distributed applications.

In a nutshell, MTS resides on Windows NT Server and acts as a container for middle-tier components—typically objects that encapsulate business rules and handle interaction between a front-end application and a back-end database. By instantiating your Visual Basic–created objects inside MTS, you automatically get the benefit of all of MTS's services, exposed as a set of properties available to your component. This means that you don't need to worry about writing your own code to deal with the messy details of object lifetime, threading, transactions, or security. Visual Basic components written for MTS can be accessed from just about anywhere: from a Visual Basic (VB) application, Visual Basic for Applications (VBA) code within a Microsoft Office application, or even from a Web page on the Internet or an intranet.

Of course, there's no such thing as a free lunch—if you want to dine at the MTS buffet you'll have to meet some basic requirements. First off, MTS assumes an n-tier architecture with the middle tier (or tiers) deployed on Windows NT Server. Second, in order to use MTS your components must be created as ActiveX® dynamic-link libraries (DLLs); the classes in those components must be Public with their Instancing property set to MultiUse. Additionally, if you want to debug your MTS components in Visual Basic, there are some additional rules you'll need to follow—more on that later.

Why MTS?

If your application meets the basic requirements, using MTS can save you a lot of unnecessary work. In the Visual Basic tradition, MTS components wrap up distributed application technologies into a nice, neat package, insulating the developer from the complexity of the underlying implementation.

Over the last couple of years, the n-tier application architecture has become increasingly popular. In theory, the concept of decoupling the business logic from the user interface by encapsulating it in middle-tier objects greatly enhances the flexibility and maintainability of your application. In practice, the n-tier approach introduces some thorny issues—not the least of which is the issue of scalability.

With the two-tiered client server approach, back-end databases such as Microsoft SQL Server™ or Oracle typically managed scalability for you, handling such things as managing connections, pooling threads, and monitoring transactions. When you move to an n-tier architecture, scalability becomes the responsibility of the middle tier. In the past, this meant that you had to create your own infrastructure to handle connections, threads, and transactions, often requiring a considerable amount of extra code.

With MTS the scalability issues are addressed by providing a standardized infrastructure or application framework. MTS acts as an Object Request Broker (ORB), automatically managing the creation and allocation of objects and threads. You no longer need to worry about object lifetime issues; MTS handles it all for you.

The same holds true for transaction management. While Visual Basic has always been able to handle transactions with the BeginTrans, CommitTrans, and RollbackTrans methods, there has never been an easy way to coordinate nested transactions. If you're working with related data in multiple tables or databases, the amount of code needed to roll back the data if one portion fails can quickly become overwhelming. With MTS, all of the details of the transaction are handled for you—a single property exposes the results of the transaction no matter how complex it may be.

An added benefit of MTS is that it exposes a role-based security model. This means that you can use a single component to provide a different set of services dependent on a user's permissions on Windows NT. For example, a salary component for a Human Resources application might expose read-only access to an employee, but the same component could expose the ability to change a salary when accessed by a payroll clerk. Trying to set up security like this in the past was difficult at best and often resulted in code that was difficult to maintain. Again, MTS manages it all for you.

By coding your components to work with MTS, you're getting a jump-start on future technologies. MTS is the first part of the COM+ initiative that will provide a distributed application framework for Microsoft Windows®. Consequently, any components that you create for MTS today will be ready for COM+ in the future.

The question you need to ask yourself when designing a new component shouldn't be "Why MTS?," it should be "Why not MTS?" Unless you enjoy writing a lot of unnecessary code, you should seriously think about creating components that work with MTS. When you consider that the infrastructure provide by MTS can replace 40 to 50 percent of the code needed for a typical distributed application, it's a no-brainer.

Exploring MTS

Most of what MTS does happens behind the scenes; the only visible element of MTS is the MTS Explorer (Figure 1). MTS Explorer runs on the server and allows you to manage and deploy MTS packages.

Figure 1. Microsoft Transaction Server Explorer.

A package in MTS contains one or more components (ActiveX DLLs). Each component can contain one or more classes which, in MTS terms, are referred to as MTS Objects. A package can be defined as either a Server Package, meaning that its components will be activated in a dedicated server process, or a Library Package, meaning they will be activated in the client's process. In Visual Basic terms, this is the equivalent of running a component out-of-process (Server) or in-process (Library).

You can add Visual Basic-created components to MTS packages by dragging and dropping them onto the MTS Explorer. The Explorer handles all of the details of registering them as MTS components. You can also use the MTS Export facility to copy an MTS package to another server for testing purposes.

MTS exposes a full object model through which you access components running inside MTS. The top-level object in the MTS hierarchy is the ObjectContext object. The ObjectContext object provides access to a single MTS Object, sometimes referred to as the root object. Once the root object has been instantiated, you can create additional objects (enlisted objects) that will run within the same context as the root object. By tying these objects together under a single ObjectContext, MTS allows you to manage multiple transactions with multiple data sources through a single object interface. If a transaction fails within any enlisted object, the error is reported back to you via the ObjectContext interface—normally if one part of the transaction fails, the entire transaction fails.

Roles, set at the package level, are central to the MTS security model. A role is an abstraction that defines a logical group of users. At development time, you use roles to define declarative authorization and programmatic security logic. At deployment time, you bind these roles to specific Windows NT groups and users using the MTS Explorer. This allows a Windows NT Administrator to change permissions for your MTS components without any need to touch the component's code. MTS also exposes programmatic security if you need finer-grained control.

The MTS Explorer also allows you to monitor transactions at run time. You can use the Transaction List window to monitor the state of an active transaction, the Transaction Statistics window to view summary statistics for recent transactions, or the Trace Messages window to view system messages related to transactions.

Using Visual Basic to Create MTS Components

Creating an MTS component really isn't much different than creating any other component with Visual Basic. As mentioned earlier, there are a few basic requirements that must be met.

The first step in creating an MTS component is to open an ActiveX DLL project, because MTS components by definition must be ActiveX DLLs. All classes within your project must have their Instancing property set to 5—MultiUse (the default setting) in order to work with MTS. In addition to multiple class modules, you can also add standard (.bas) modules to your project to store shared code such as Constants.

In order to use the MTS object model, you'll need to add a reference to the Microsoft Transaction Server Type Library (mtxas.dll) in the Project References dialog box. Also, MTS requires that Visual Basic components use Apartment threading, so you'll want to make sure that Apartment Threaded (the default) is selected in the Project Properties dialog box.

Visual Basic 6.0 provides a new property, MTSTransactionMode, that allows you to set the transactional attribute for each class within your component. This property maps directly to the transaction attributes in MTS, as shown in the following table.

Visual Basic property value MTS transaction attribute
0—NotAnMTSObject N/A
1—NoTransactions Does not support transactions
2—RequiresTransactions Requires a transaction
3—UsesTransactions Supports transactions
4—RequiresNewTransaction Requires a new transaction

The value of the MTSTransactionMode property automatically sets the transaction attribute for the object when the component is added to MTS. You can set the property to different values for different classes within your component depending on your requirements. For instance, your base class (root object) might require transactions while additional classes (enlisted objects) might only use transactions in certain circumstances. Note that the MTSTransactionMode property only applies when your component is running under MTS. Even if you set the property to RequiresTransactions, the component can still be run independent of MTS providing that you add code to test for the presence of MTS in your code.

Any procedures within your classes that will use MTS require that you first instantiate an ObjectContext object using the GetObjectContext method, as in the following example:

Public Sub MakeATransaction(Something As String, Amount As Double)
   Dim ObjCtx As ObjectContext
     Set ObjCtx = GetObjectContext()
   … (Add your transactional code here)
   ' If the transaction fails call the SetAbort method
   If condition = False Then
      ObjCtx.SetAbort
      Err.Raise … (Add error info here)
   Else
      ' If it succeeds call the SetComplete method
      ObjCtx.SetComplete
   End If
End Sub

Within your procedural code you can inspect any of the ObjectContext's properties or call any of its methods. For example, you might call the IsCallerInRole method to determine if the current user belongs to a defined role, and then branch your code accordingly. In all cases, you'll want to check to see if the transaction succeeds. If it fails, use the SetAbort method to cancel the transaction, otherwise use the SetComplete method to tell MTS that you are done. When the SetAbort method is called on an enlisted object, you should also raise an error and handle it in the root object, aborting the root transaction if necessary.

Once you have finished coding your MTS component, you'll need to compile it and set binary compatibility for the .DLL. If you don't set binary compatibility and you later add or remove interfaces, you will end up with incompatible versions of your component.

Creating MTS Clients

Creating a client application that uses MTS components is pretty much the same as creating any other client application. You don't need a reference to MTS itself, however you do need to reference any MTS component that will be called from your code. Your client can use MTS components created using any programming language, not just Visual Basic, and your client can take many shapes—a stand-alone Visual Basic application, a Web application, or even another component.

Of course, if a user tries to run a client that uses MTS and the user isn't connected to an MTS server, you've got a problem. You can easily anticipate this situation by wrapping your call to GetObjectContext in an error-handling block. If MTS transactions are required, you can present an error message and tell the user how to get connected. If you don't absolutely require transactions, you can branch your code and ignore the MTS-related code. This approach works great for applications such as sales force automation, where a salesperson might create orders offline and later submit those orders in batch using transaction processing.

Debugging MTS Components

Beginning with Visual Basic 6.0 it is possible to debug components running in MTS from inside the Visual Basic environment. There are, however, a few limitations that you'll need to be aware of.

Most importantly, in order to debug in Visual Basic the server must be running Service Pack 4 (SP4) for Windows NT 4.0 on both the server and the client machine. Debugging is not supported on Windows 95 and Windows 98. Debugging is only available for a single client and a single MTS server component at a time; if you need to debug multiple clients or multiple MTS servers you'll need to debug in Microsoft Visual C++®.

To debug an MTS component in Visual Basic, you must first make sure that the MTSTransactionMode property is set to a value other than 0—NotAnMtsObject. If the property is set to a valid value, when you hit F5 to begin debugging, the object will be activated inside of the MTS run time. You can then step through your code from client to server and back again, just as you would with any other component.

Another thing to keep in mind is that you should avoid putting code in the Class_Initialize or Class_Terminate events if you intend to debug a component in Visual Basic. The Visual Basic run time calls Class_Initialize before the ObjectContext object is activated in MTS, so any operations that call into the object inside Class_Initialize will fail. Likewise, code in the Class_Terminate event could attempt to reactivate the object inside MTS, an attempt that will fail and cause Visual Basic to stop. It's best to avoid putting code in these events if at all possible.

For additional tips on debugging MTS objects with Visual Basic, see "Building and Debugging MTS Components in Visual Basic 6.0" in the Visual Basic 6.0 ReadMe file.

Your World Is MT Without MTS

The decision to use MTS should be an easy one. If you're doing component-based development with Visual Basic, you should be building components that will work with MTS—period. Even if you aren't using it today, the amount of extra work to make your components MTS-aware is negligible; by doing a little extra work today, your components will be ready for COM+ when it arrives.

Of course, this is just an introduction to integrating MTS and Visual Basic. For more in-depth information, the following articles are available on the MSDN Library: "Building and Debugging MTS Components with Visual Basic 6.0" and "Creating MTS Components and Converting Existing Components to Use MTS."

I've seen the future, and it is MTS.