Remoting Duwamish Components

Duwamish Books, Phase 3.5

Dale Smith
Microsoft Developer Network

November 1998

Summary: Explains the process of preparing the applications and the Component Object Model (COM) components, Business Logic Layer (BLL) and Data Access Layer (DAL), to work on different computers. (7 printed pages) Discusses:

Introduction

In Phase 3 of the Duwamish Books sample, we removed much of the business logic from the individual applications and placed it into a single shared component. (See my article, "Breaking Out the Business Logic Layer," for a look at issues with removing the code from the client-side applications, and Steve Kirk's "Abstracting Business Transactions" for a discussion of the design of the business logic component.) Phase 3.5 implements Microsoft® Transaction Server (MTS) to distribute the Phase 3 logical three-tier architecture across a number of computers. Michael Zonczyk reveals the reasoning behind the decision to use MTS in "Hoisting the Duwamish Components into MTS." In this article I will describe the issues associated with distributing the components among various computers.

Object Instantiation and Transaction Context

The first issue to consider when attempting to distribute a component is what roles its objects will play in the application and where the component fits into the distribution scheme. In Microsoft Visual Basic® there are three techniques that can be used to instantiate an object: New, CreateObject, and CreateInstance. In deciding which of these to use, you must consider whether the component will be an MTS component, whether it will be in the scope of a transaction, and whether the component can be replaced without the need to recompile the client.

Use the New keyword or CreateObject when instantiating an object that will run outside of MTS.

Use CreateObject if the component is running under MTS and your object will not need to support transactions.

When an object must be in the scope of a transaction, it must use the caller's context object to instantiate the object with the CreateInstance method. If a component is installed as an MTS component and its MTS transaction attribute (property) is set to Requires a Transaction, when it creates an object MTS automatically creates a new transaction if its caller doesn't have a transaction. Any object created with the CreateInstance method will execute in the scope of the caller's transaction.

Note   Using the New keyword from within MTS will result in creating a non-MTS object, even if the class is configured for MTS.

Table 1. Three Ways to Instantiate an Object

Technique Use Binding Implications
The New keyword Dim myObj as New <myClass>

-or-

Dim myObj as <myClass>
Set myObj = New <
myClass>

Early
  • If used from within MTS and myClass is an MTS class, creates a non-MTS-aware object.

  • If used outside of MTS and myClass is an MTS class, creates an MTS-aware object.
  Dim myObj as Object
Set myObj = New <
myClass>
Late
  • If used from within MTS and myClass is an MTS class,
    creates a non-MTS-aware object.

  • If used outside of MTS and myClass is an MTS class, creates an MTS-aware object.
The CreateObject function Dim myObj as <myClass>
Set myObj = CreateObject(<
myClass>)
Early
  • Can be called from within MTS or outside of MTS.

  • Transaction context not passed.
  Dim myObj as Object
Set myObj = CreateObject(<
myClass>)
Late
  • Can be called from within MTS or outside of MTS.

  • Transaction context not passed.
The CreateInstance method Dim myObj as <myClass>
Set myObj = <objectcontext>.CreateInstance(<
myClass>)
Early
  • MTS-only method.

  • Executes within the scope of the caller's transaction.
  Dim myObj as Object
Set myObj = <objectcontext>.CreateInstance(<
myClass>)
Late
  • MTS-only method.

  • Executes within the scope of the caller's transaction.

Duwamish Implementation

In Duwamish Books, we had to decide which of the object instantiation options to use for each of three distinct parts of the sample: the clients, the Business Logic Layer (BLL) component, and the Data Access Layer (DAL) component.

The clients

Each of the client applications has a common class named CWorkFlow. Methods in the CWorkFlow class call methods in the BLL. At design time, we used early binding to allow for the Microsoft IntelliSense® feature. Because we wanted to be able to "swap" a Visual Basic version of the BLL component with a Visual C++® version and still use the same installed client, we chose to compile the client with late binding. (We followed the same procedure with the BLL as well.) Because the client apps are not MTS components, we could have used either New or CreateObject to instantiate the BLL. We chose to use CreateObject as the preferred method for late binding. When early binding is used, the class name is resolved into a globally unique identifier (GUID) at compile time, whereas late binding resolves at run time. Consequently, late binding allows the swapping of the component by class name without the need to recompile the project. In Duwamish, this is implemented with conditional compile directives, as shown in the following code:

#If mbEarlyBoundBLL Then
    Dim moBusLogic As d35bll.cBusLogic
#Else
    Dim moBusLogic As Object
#End If

Private Sub Class_Initialize()
   Set moBusLogic = CreateObject("d35bll.cBusLogic")
End Sub

The Business Logic Layer component

The BLL methods call methods in the DAL. In Phase 3, transactions were handled in the DAL with a number of methods that stepped through the transaction from open to commit or rollback. For a discussion of the Duwamish Books business logic and the basic design of the BLL component, see "Abstracting Business Transactions." Because the transaction processing has been changed from explicit control in the DAL to letting MTS process the transactions in the BLL, some calls have to be made in the context of a transaction while others do not. Those that must be in the context of a transaction are instantiated with the CreateInstance method. Those not needing transaction context can use either CreateObject or CreateInstance to instantiate the DAL.

One approach is to perform a straightforward conversion of the BLL to run under MTS. In Duwamish Books, some of the methods require a transaction, so it is necessary to set the cBusLogic MTS transaction attribute to Requires a Transaction. Because some of the calls made to the DAL require a transaction, it is easiest to just use the CreateInstance method to instantiate the d35dal.CdataAccess object, whether the BLL methods are in the context of a transaction or not, as follows:

'a new instance of CDataAccess class under MTS
Set m_oDal = GetObjectContext.CreateInstance(scDAL_OBJECT_NAME)

' Execute stored procedure and return recordset
m_oDal.GetRecordset scCONNECT, sCmd, oRs

However, to avoid creating unnecessary transactions, you could do a little more work and split the existing BLL methods into two classes, which we have done in the Visual Basic version of the BLL. One of the two classes, cBusLogicT (in our sample), is used for transaction-only operations (set its MTS transaction attribute to Requires a Transaction) and the other, cBusLogicN (transaction attribute = Supports transactions), for those that do not necessarily need a transaction. Setting the transaction attribute to Requires a Transaction causes MTS to provide automated transactions. (See the "Using Automatic Transactions" section of "Hoisting the Duwamish Components into MTS.") Because we want to make the BLL compatible with our existing application, we use an additional class, cBusLogic (transaction attribute = "Does not support transactions"), which we use as a pass-through router calling those methods in the other two classes that are relevant as transactional or nontransactional methods.

For a more detailed discussion of the various approaches one could take to modify the BLL component for use with MTS, see Michael Zonczyk's article "Hoisting the Business Logic and Data Access into MTS." He also discusses the implications associated with transactions handled by MTS in the section "Designing for MTS Transactions".

The Data Access Layer component

The DAL contains two methods: ExecQuery and GetRecordset. Because these methods will be called from the BLL and, therefore, should be considered in the context of a transaction, the DAL transaction attribute should be set to Supports transactions in the MTS Explorer. Connection and Recordset, which are not MTS objects, are instantiated by the DAL with CreateObject.

For a more thorough explanation of transaction attributes, see the "Transaction Attributes" section of "Hoisting the Business Logic and Data Access into MTS"

Location Transparency and Component Distribution

When distributing a component you also need to consider where the component will reside and how a client determines where its server component is.

Actually, the client doesn't determine the component's location—at setup time the user registers the component on the client computer as a remote server. So, at setup time the machine hosting the client applications registers the BLL component, and the machine hosting the BLL, which is client of the DAL server, registers the DAL component. As was described previously, each of the client applications (front-end) is a client of the BLL, and the BLL is a client of the DAL. These two components can be on different servers. The client can be on still another computer.

While there are a number of ways to distribute components, such as through distributed COM (DCOM), in Phase 3.5 we decided to use MTS to "wrap" the components, which enhances DCOM. Why? As stated in Microsoft Transaction Server version 2.0 documentation: "MTS ... makes it easier to build distributed applications by providing location transparency. MTS automatically loads the component into a process environment. An MTS component can be loaded into a client application process (in-process component), or into a separate surrogate server process environment, either on the client's computer (local component) or on another computer (remote component)."

Let's look at the setup for the component on the client's computer. Refer to the Help file associated with the MTS 2.0 Explorer for further explanation of the following steps for manually distributing a single component. In the next section I will describe the automated process, which we used in our Setup application for Duwamish.

Components

  1. Install it (copy it to the server computer).

  2. Register it (use Regsvr32.exe).

  3. Start the MTS Explorer.

  4. Create a package.

  5. Set the package properties.

  6. Install the associated component(s).

  7. Set the component properties.

  8. Export the package (produces an application for client setup).

Client

This process generates an application executable for the remote client computer running Microsoft Windows NT® or Windows® 95 (with DCOM support) to access the server component(s). When the executable "exported package" is run on the client computer it does the following:

  1. Creates a folder—Remote Applications—if it doesn't already exist, in the Program Files folder.

  2. Creates a folder—in the form of a GUID {01BCBCD2-6A96-11D2-B66C-00AA0051273C}—in the Remote Applications folder, and copies the server DLL to this folder (for typelib info).

  3. Creates registry entries:

    progID

    classID

    appID

Of particular interest is the appID registry entry—RemoteServerName. This can be changed directly in the registry or changed with DCOM configuration (Dcomcnfg.exe) to allow the server application to be located on a different server.

Setup Issues

Steve Kirk describes the physical setup used in our sample in "Installing a Distributed Application."

MTS administration objects

As was outlined earlier, manually distributing MTS components can be an involved process. Because there are many things to keep track of, this approach is prone to produce errors. What we would like is to have this process done automatically during the installation process. This is where the MTSAdmin API comes in. Some of the steps taken to manually distribute the components can be accomplished by a setup application that incorporates the MTSAdmin API. The following code segment (from the Duwamish Setup) demonstrates automating package and component setup as just outlined in steps 4-7 of the manual component setup:

' First, we create the catalog object
Dim catalog As New MTSAdmin.catalog
    
' Then we get the packages collection
Dim packages As Object
Set packages = catalog.GetCollection("Packages")
packages.Populate

' We now add the new package to the packages collection
Dim newPack As Object
Set newPack = packages.Add
newPack.Value("Name") = PackageName
packages.SaveChanges

' Next we add the new component(s) to the package
Dim components As Object
Set components = packages.GetCollection( _
"ComponentsInPackage", packages.Item(miCount).Value("ID"))
    
' Install components. 
Dim util As Object
Set util = components.GetUtilInterface
' Note: dllpath is the file path to the DLL containing the components
util.InstallComponent dllpath, "", ""

' Commit the changes to the components
components.SaveChanges

Conclusion

There are other ways to distribute components. But you must ask yourself: Because technology is changing progressively, should you use the most up-to-date and recommended way to develop and implement a distributed application? Microsoft Transaction Server provides an excellent development and run-time support system for producing a distributed multitier application. The MTS Admin API simplifies remote component setup, as well. Scalable, distributed applications are becoming easier and easier to implement. I can hardly wait for the next wave of technology to hit!