Adapting Components to MTS

[Previous] [Next]

Let's take a look at an example from our sample application. Given the name of the application server to which we're adapting our application—Microsoft Transaction Server—it seems logical to start with a transactional component. Since we have only one in the sample, let's begin with that one: the HorseTrSrvcs component.

Implementing IObjectControl

First of all, let's have the HorseTrSrvcs class4 implement the IObjectControl interface. To do that, you must make your Visual Basic project reference the MTS Type Library. You set up this reference by clicking References from the Project menu in Visual Basic and then selecting the library in the References dialog box. Figure 14-1 shows you how to reference this library.

click to view at full size.

Figure 14-1. Your Visual Basic project must reference the MTS Type Library to be able to use MTS features.

After referencing the MTS Type Library, you can begin taking advantage of MTS features.

Implement IObjectControl

The following code snippet implements the IObjectControl interface (after setting Option Explicit). It also declares a couple of variables, which we're soon going to use.

Option Explicit Implements MTxAS.ObjectControl Private mobjCTX As MTxAS.ObjectContext Private mblnInMTS As Boolean

Code for the Activate method

The next thing is to create code for the Activate method. One of the things you might want to do here is get a reference to the object's context object, saving that reference in a variable. Before we look at the code, let's make a statement. We know we've said this before, but it's so important that we'll say it again: When the Class_Initialize event happens for an object, the object doesn't yet exist. Since it doesn't exist, it doesn't have a context object, even though it's supposed to run under MTS.

So the Class_Initialize event is not the right one to get the reference to the context object. You should use the ObjectControl_Activate method for that task because when MTS calls that method, the context object exists if the object runs under the control of MTS. Here follows the code that saves a reference to the context object and also finds out whether the object runs under MTS:

Private Sub ObjectControl_Activate() Set mobjCTX = GetObjectContext() mblnInMTS = Not (mobjCTX Is Nothing) End Sub

The second line might look a bit funny. It saves True or False in the mblnInMTS variable. The line evaluates the expression Not (mobjCTX Is Nothing). If, after execution of the first line, the mobjCTX variable is still nothing, the component isn't running in MTS. If so, False is saved in the mblnInMTS variable. If, on the other hand, mobjCTX is not Nothing, the component really is running in MTS. Then True is saved in the mblnInMTS variable.

The Deactivate method

Because object pooling isn't available in MTS, we won't use it here. So we can leave the Deactivate method empty.

Private Sub ObjectControl_Deactivate() End Sub

The CanBePooled method

MTS calls this method after calling Deactivate and before the Class_Terminate event is fired. As we've already explained in this chapter, its only use is to tell COM+ whether to pool the object. Because this chapter is all about MTS, we can safely assume that you don't want to pool your objects. We also assume that you're not willing to risk having COM+ automatically pool them after a future upgrade to Microsoft Windows 2000. The safest way to avoid that is to deny object pooling, which is exactly what the following code does:

Private Function ObjectControl_CanBePooled() As Boolean ObjectControl_CanBePooled = False End Function

Calling the SetComplete and SetAbort Methods

When an MTS object has finished its work, it should tell MTS about it, allowing MTS to recycle the resources that were used by the object to other objects and other users. There are two ways for an object to tell MTS that it's finished. Both of them use the context object, calling the SetComplete or SetAbort method of that object. The only difference between SetComplete and SetAbort is that SetComplete is a happy call—"I made it!"—whereas SetAbort is an unhappy call—"I failed!" Only when transactions are involved is it important to distinguish between the two calls.

If an MTS object is involved in a transaction and fails, it must call SetAbort. This will set the Doomed flag to True, thus imposing an irrevocable death sentence on the transaction. Whatever happens with this transaction from now on, MTS will always end it by rolling it back. It won't matter how many of the objects involved said SetComplete; if only one of them says SetAbort, the transaction fails and is rolled back. This you can trust! In all other cases, except those in which you want your object to stay alive a little longer, you should call SetComplete. We'll describe those cases in the "Revisiting the Stateful Model" section of Chapter 23, "Some Final Issues."

So your best strategy is to nearly always call SetComplete if all went well and to call SetAbort after an error. We say nearly always because, as we just mentioned, sometimes you need the object to stay alive for a while longer and then you shouldn't call either SetComplete or SetAbort. In these cases, you should call EnableCommit or DisableCommit instead. These methods correspond to SetComplete and SetAbort, respectively, setting the "happy" flag to True or False, but they don't cause the object to be deactivated. It stays alive, allowing the client to call one of its methods again.

To complete this description of these particular methods, SetComplete and SetAbort make an object go stateless and into deactivation; EnableCommit and DisableCommit make the object retain its state and go on living. This is your loophole for using a stateful object, but we suggest you don't use it unless absolutely necessary.

Calling SetComplete and SetAbort if in MTS

Turning back to our code, here's the Save method of the HorseTrSrvcs component again. This time the code has been enhanced with MTS calls. We'll focus on the calls to SetComplete and SetAbort this time and come back to the other parts in just a little while.

So please focus on the following code in boldface type for a start:

Public Sub Save(rs As Recordset) Dim objADOSrvcs As RaceADOSrvcs On Error GoTo SaveErr ' Get New IDs. If mblnInMTS Then Set objADOSrvcs = _ mobjCTX.CreateInstance("RaceDataAccess.RaceADOSrvcs") Else Set objADOSrvcs = _ CreateObject("RaceDataAccess.RaceADOSrvcs") End If rs.MoveFirst While Not rs.EOF If rs.EditMode = adEditAdd Then rs!HorseId = objADOSrvcs.GetNewNumber("Horses") End If rs.MoveNext Wend rs.MoveFirst rs.ActiveConnection = strConn rs.UpdateBatch  If mblnInMTS Then mobjCTX.SetComplete Exit Sub SaveErr:  If mblnInMTS Then mobjCTX.SetAbort Err.Raise Number:=Err.Number, Source:=Err.Source, _ Description:=Err.Description End Sub

As you can see, the Save method calls the SetComplete method as the last act in an error-free execution; it calls SetAbort whenever an error happens. In this case, we don't separate different errors from one another; we just assume that an error is a good reason to abort the transaction. And aborted it will be. The transaction is rolled back even if the object that owns the transaction says SetComplete. Remember—one single SetAbort is all it takes to abort even the largest and most complicated transaction.

Another thing of interest is the fact that the preceding Save method calls the SetComplete or SetAbort method only if the object is in fact running under MTS. If it's not, the mblnInMTS variable will be False, and then these context object methods won't be called. By the way, the demonstrated way of deciding whether to call SetComplete or SetAbort isn't the only way to go about it. Some developers prefer to use compiler directives instead to affect the SetComplete and SetAbort behavior of their MTS objects.

Creating Other MTS Objects

Let's take a look at another part of the Save method: the one that creates a RaceADOSrvcs object to call for new ID numbers. Here's an extract from the same code segment you just saw:

 If mblnInMTS Then Set objADOSrvcs = _ mobjCTX.CreateInstance("RaceDataAccess.RaceADOSrvcs") Else Set objADOSrvcs = _  CreateObject("RaceDataAccess.RaceADOSrvcs") End If

This code snippet also uses the mblnInMTS variable to find out whether the object is running in MTS. If it is, the code uses the CreateInstance method of the context object to create the new object. If the object isn't running in MTS, the code uses the standard CreateObject function instead. The big difference is the following:

  • The CreateInstance method stays inside MTS to make the new object share the same context as the caller. If the caller, for instance, runs in a transaction, and if the new object requires or supports transactions, the work of the new object will be part of the same transaction as the caller.
  • The CreateObject method doesn't stay inside MTS; it goes out to COM to create the new object. As a consequence, even if the new object as well as the caller runs in MTS, the new object gets a new context of its own. This is because COM—as opposed to MTS—always creates a new context when it creates an MTS object. Therefore, the CreateObject method makes sure that the new object won't run in the same transaction as its creator. If the new object requires transactions, MTS starts a new transaction for it, even if the creator runs in another transaction. If the new object just supports transactions, MTS won't start a new transaction for it, and the new object won't be assigned to the caller's transaction either. The new object will run on its own, outside any ongoing transaction.

You should always use the CreateInstance method of the context object when you want one MTS object to create another. If you use the CreateObject method, you run the risk of having the caller, for instance, credit one account inside one transaction and the new object debit another account inside another transaction. One of them might fail, the other one succeed. Money might be made, and money might disappear. And it's all because of you….

Setting the Transactional Attribute

For each component installed in MTS there is a transactional attribute, set to one of four values. Figure 14-2 shows the dialog box in which you set this value.

Figure 14-2. The four transactional attribute values. You must select one for each component installed in MTS.

Visual Basic 6.0, in contrast with Visual Basic 5.0, allows you to set the transactional attribute in Visual Basic's IDE. Figure 14-3 is a snapshot of the Properties window for the HorseTrSrvcs class.

In Figure 14-3, you can see how the transactional attribute of the class is set to RequiresTransaction. Later, when you install the component in MTS, this value will initially be set as the default value of the component's transactional attribute in MTS. You can easily use MTS Explorer to modify it if you want, but normally you should be able to stick with the value you select in Visual Basic.

Figure 14-3. You can set the MTS transactional attribute value in Visual Basic 6.0's IDE.

Debugging in Visual Basic's IDE

If you run Windows NT Service Pack 4 or later, setting the transactional attribute in Visual Basic allows you to debug the source code and make the MTS calls, even though you won't run the code inside MTS. This practice is extremely convenient for you as a Visual Basic developer, and it beats using the Microsoft Visual Studio debugging environment by horse lengths (pun intended), at least as long as we're talking ease of use.

Admittedly, the Visual Studio debugger better simulates the real MTS run-time environment than the Visual Basic debugger does. The Visual Studio debugger makes real calls to MTS in general and to the context object in particular, and it uses the real registration of the components involved. Furthermore, it's not as restricted in the threading apartment as the Visual Basic debugger (being single-threaded) is, but it's also far more complicated to use.

If you use Compuware NuMega's SmartCheck, you have a third debugging alternative. You can follow the instructions Compuware NuMega gives in a technical paper to allow SmartCheck debugging inside MTS. If you already use NuMega's tools, just ask them for a copy of this white paper or download it from their Web site. If you don't, it might be a good idea to have a talk with them to find out what their different tools can do to help you increase quality control in your Visual Basic projects.



Designing for scalability with Microsoft Windows DNA
Designing for Scalability with Microsoft Windows DNA (DV-MPS Designing)
ISBN: 0735609683
EAN: 2147483647
Year: 2000
Pages: 133

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net