Platinum Edition Using Visual Basic 5

Previous chapterNext chapterContents


- 42 -
Using Visual Basic with Microsoft Transaction Server

Distributed transaction processing involves spreading the logical and business rule processing over multiple computers. You learn how this works, and how it requires you to design your applications differently from traditional client/server applications.
Microsoft recently released its own Transaction Server for use in building distributed transaction architectures. You see how Transaction Server works, and how it uses Microsoft's Distributed Common Object Model (DCOM) technology to provide a transparent platform for building distributed applications.
One key advantage offered by Microsoft's Transaction Server is the ability to build modules by using Visual Basic (along with Microsoft's other development languages). You learn what is involved in building modules with Visual Basic that integrate with Transaction Server.

There has been a lot of discussion in the client/server industry over the past few years about second-generation and three-tier architectures. These are applications infrastructures where the applications processing is not divided into the traditional two parts--the front-end user interface and the back-end database--but into at least three separate and distinct parts. There are several reasons that this application architecture is superior to the original client/server model:

One key difference between traditional, or two-tier, client/server models and second generation, or three-tier (also known as n-tier, where n is any number greater than two), client/server architecture is in the use of what is known as middleware. Some of the most popular kinds of middleware are Transaction Monitors and Object Request Brokers (ORB). Microsoft's Transaction Server is a combination of these two technologies, providing you with a flexible and powerful middleware component that can be used to build very large-scale distributed processing applications with minimal coding and configuration effort on your part.


NOTE: The typical three-tier architecture is composed of the following three tiers:

In this chapter, you take an in-depth look at Transaction Server, how it works, and how you can use it in building applications. You also look at what's involved to use Transaction Server with Visual Basic, and how you can design and code Visual Basic objects that can be integrated into Transaction Server to provide a large, distributed application. In the chapters immediately following this one, you look at how Transaction Server can be integrated into a Web site to provide the ability to build an integrated enterprise-wide application system that stretches far beyond what most Web sites currently are capable of.

Understanding Distributed Transaction Processing

Imagine that your company has several independent database systems: one for maintaining the current inventory in the company warehouse, one for maintaining all items that have been requisitioned and should be arriving on the receiving dock, and a third database for maintaining customer orders and shipping information, as shown in Figure 42.1.

FIG. 42.1
A typical catalog company has systems to track current inventory, requisitioned or back-ordered items, and shipping orders.

In this company, when an order comes in, the order is entered into the order entry system, which produces a shipping order. This order is then taken to the warehouse, where each item in the order is removed and taken to the shipping dock to be packaged and sent to the customer. While at the warehouse, the employee filling the order finds that one or two items are out of stock. The employee then takes the order to the requisition department and has the missing items back ordered for later shipping.

At each of these points, the individual systems have to be updated so that they are up-to-date. Even the Shipping Dock system has to be updated to produce an accurate shipping bill-of-lading, which correctly reflects the back-ordered items.

This seems like a lot of wasted and duplicated effort. Wouldn't it make a lot more sense to connect all these systems so that they could all exchange the necessary information to perform most of these tasks themselves? If you put a network in place and connect all these systems to the network, as in Figure 42.2, the potential for these systems to exchange the appropriate information can be realized.

Unfortunately, anyone who has attempted to connect separate systems in this way knows that after you have all the systems on a network, the work is just beginning. Enabling all these systems to work together is what middleware is all about.

FIG. 42.2
A network can be used to connect all the company systems so that they can exchange information with each other.

Transaction Monitors

The original idea behind the client/server computing model was to split the application processing between two computers. This split was normally made where the database processing was all performed on the server, while all application processing was performed on the client, as in Figure 42.3. This model works reasonably well, except that it is fairly easy to overload the client system with data, if the application allows the database to return more data than the client computer can handle. (Remember, this model was used when the standard desktop computer was a 386 with 4M of RAM, and a 486 was a high-end workstation.)

FIG. 42.3
The original idea behind client/server computing was to split appli- cation processing between the client and the database server.

As the number of active users of these early client/server systems increases, the workload on the database increases. A normal application maintains an open connection to the database server for the entire time the application is running, as in Figure 42.4. This requires the database server to have additional processing power to service all those open connections, even if they are sitting idle. This also increases the cost associated with the database itself, as most traditional database license prices are based on the number of users that can be connected to the database simultaneously.

FIG. 42.4
As the number of users increases, the number of active connections that must be serviced by the database increases.

As the number of databases that an application needs to interact with increases, the number of open connections that the application must maintain also increases. This adds to the processing load on the client computer, as well as the network management and configuration. The total number of connections that must be maintained can be calculated as the number of clients times the number of database servers to which the client connects (see Figure 42.5).

This is a lot of configuration information that has to be maintained for each client computer. If one of the databases that an application uses has to be taken offline and the backup database brought online, the configuration of each and every client that connects to the database has to be updated to reflect the new database server (with care taken not to update the wrong database server information).

FIG. 42.5
The total number of open connections that must be maintained can be calculated as the number of clients times the number of servers.

This is one of the primary problems that Transaction Monitors were designed to solve. A Transaction Monitor goes between the client systems and the database servers, as in Figure 42.6. Each client maintains a single connection to the Transaction Monitor instead of the database servers. Likewise, the Transaction Monitor maintains connections to each database server. This allows the Transaction Monitor to act as a traffic cop, passing each database query or update to the appropriate database, and to maintain only as many open database connections as are currently required, which allows the database servers to run more efficiently.

FIG. 42.6
A Transaction Monitor reduces the number of open connections to the number of clients plus the number of database servers.

Object Request Brokers

Object Request Brokers (ORBs) fall into a different category of middleware from Transaction Monitors. ORBs provide location transparency to application modules and services. What location transparency means is that when an application needs to interact with a server process, the application does not need to know where that specific server process is located on the network. The only process that the application knows the location of is the ORB client stub located on the same machine as the application.

The ORB knows where all server processes are running, or on which machines the server processes can be run. If a particular server process is not running when an application requests access to it, the ORB starts the server process on one of the machines for which the process is configured to run. The client application does not know on which server the requested process is running, or if it's even running. The ORB keeps track of all server processes for the client applications. The ORB can even perform load balancing between two or more servers running the same process so that each server is servicing around the same number of requests.

Returning to your catalog sales company, it is reasonable to expect that the order entry system would use various services that could be located on a series of servers on the company network. One of these services could be a sales tax engine, which calculates the tax for each of the customer orders. It makes sense to keep this set of calculations on a server, as tax laws have a tendency to change. By keeping this processing module on the server, it would be a lot easier to update in this one location every time the tax laws changed, as opposed to having to update every order entry workstation in the company.

Another service the order entry systems might take advantage of is a credit check application. By having all the credit authorization requests go through a single server, it would be easy for that one server to maintain an open connection to the credit clearinghouse, as compared to outfitting each workstation with a modem and the software to call up the credit authority (not to mention all the additional phone lines this would require).

A third service the order entry systems might use is the business rules engine. This system would calculate shipping costs based on the quantity or weight of the ordered items or enforce minimum purchase rules. By keeping this module on one or two servers, you could easily update the module as the powers-that-be within the company change the business rules that this module has to enforce.

Considering that the functions provided by these server modules are critical to the core business of your catalog sales company, it's important to make sure that these modules are always available for the order entry systems. To make sure that these systems have high availability, they are probably loaded onto more than one system. If the order entry application made direct accesses to these services, each copy of the application would have to know which machines each service is running on at all times. By using an ORB, the individual copies of the order entry application don't know and don't care what servers any of these services are running on. The ORB takes care of making the connections and passing the results back to the order entry application, as seen in Figure 42.7.

FIG. 42.7
The ORB relieves the client application from having to know which server computer each service is running on.

Introducing Microsoft Transaction Server

Microsoft's Transaction Server is somewhat of a cross between a Transaction Monitor and an ORB, although it tends to lean more toward the ORB set of functionality. If you are running an application on a Windows NT system that has Transaction Server installed and you are using ODBC to access a database, Transaction Server transparently inserts itself between your application and the database to manage that connection (as well as all the other open connections to the same database). Transaction Server also allows application functionality to be built as a series of ActiveX DLLs and distributed across a network. You take a quick look at how Transaction Server provides this functionality.

Managing Database Connections

When an application is using the ODBC interface to access a database, Transaction Server takes control of the database connection to provide a more consistent access, quicker connection, and transaction control. By placing itself between the application and the database, Transaction Server can open its own connection to the database and provide the application with a connection to Transaction Server instead of the database. This allows Transaction Server to limit the actual number of database connections to only as many as are necessary to service all the application requests, as seen in Figure 42.8. This relieves the work of maintaining all those connections from the database, allowing the database to perform better and be more responsive.

FIG. 42.8
By inserting itself between the applications and the database, Transaction Server can limit the number of active connections that the database has to service.

When an application closes its connection to the database, Transaction Server maintains the open database connection so that the connection can be reused either by the application that closed the connection, or by another application (or client) that needs the same connection to the database. This allows application modules to be written in such as way that they maintain only open connections to a database for those periods of time that the application really needs to have the connection open. The performance penalty for closing and reopening a database connection is removed, making it more attractive to write applications that release the associated resources when they are not needed.


NOTE: By combining Transaction Server with OLEISAPI2 applications, the remaining bit of overhead that you want to eliminate by using a SAPI interface is removed by allowing the OLEISAPI2 application to disconnect each time it finishes servicing a client call, and to reopen the database connection with each new call.

Managing Distributed Objects

Transaction Server provides a facility for building distributed applications by allowing you to build functionality into a series of ActiveX server DLLs and then distribute them across your network. Transaction Server keeps track of where each DLL is located and performs all the communications between them and your application. This allows you to move your functionality modules to the most suited computer on your network, based on the processing load that each module requires to service all the requests from applications needing the services of the module. You can even double up and place the same module on multiple computers and allow Transaction Server to load balance between the copies.

Transaction Server also provides you with the ability to easily mix and match modules of functionality that are built in a number of different programming languages. Any language that can be used to build ActiveX Server DLLs can be used to build modules to be used with Transaction Server. This includes not just Microsoft's Visual Basic, Visual C++, and Visual J++, but also Borland's Delphi, Symantec's Café, and Micro Focus's Visual Object COBOL. This enables you to pull functional modules together into a large distributed application, regardless of what language was used to build the individual modules, as seen in Figure 42.9. This capability of Transaction Server allows you to build an extensive application using best-of-breed modules and components.

Transaction Coordination

One of the many beneficial features of Transaction Server is its capability to provide coordinated transaction control through many objects and over multiple databases. Transaction Server accomplishes this by using the Microsoft Distributed Transaction Coordinator (DTC). The DTC was first released as part of SQL Server 6.5 and is included with Transaction Server. It provides a low-level infrastructure for distributed transactions, controlling and guaranteeing the outcome (either commit or rollback) across multiple databases and database connections. The DTC uses a two-phase commit protocol to ensure the outcome of these transactions.

FIG. 42.9
By using Transaction Server, you can use functional modules that were built by using many different languages together in a single application.


Two-Phase Commit
A two-phase commit is where a data change (insert, update, or delete) to two or more databases absolutely has to be successful in all, or unsuccessful in all. If the situation dictates that the changes to the data cannot be committed in one of the databases without being committed in the others, then two-phase commit is necessary.

Integrating Visual Basic Classes with Transaction Server

When building server objects for use with Transaction Server, a couple of basic details have to be taken into consideration. These details affect the design of your objects and add a small amount of Transaction Server-specific code. You look at each of these aspects as you build a simple Server object. You'll add on to this object with additional objects a little later.

Initializing the Visual Basic Project

All server objects for use with Transaction Server have to be built as ActiveX DLLs, regardless of which programming language is used. After you have started a new Visual Basic project with the target being an ActiveX DLL, you need to include a reference to the Microsoft Transaction Server Type library by choosing Project, References, as seen in Figure 42.10.

The object that you are building is the warehouse inventory adjuster object for your catalog order company. For now, you'll be using just two database tables in a SQL Server database. These tables are the Products table, shown in Table 42.1, and the Inventory table, shown in Table 42.2. You'll be adding additional tables as you expand the scope of this system later in this chapter. The SQL to create these tables and populate them with some initial data can be found on the CD.

FIG. 42.10
You need to include a reference to the Transaction Server Type library when building a server object for use with Transaction Server.

Table 42.1 The Products Table

Column Name Data Type Size
Prd_ID int
Prd_Name char 8
Prd_Desc varchar 40
Prd_StandardStock smallint


NOTE: The int and smallint data types in SQL Server are a fixed size and thus do not require a column size to be specified. An int data type is defined as 4 bytes, and a smallint is defined as 2 bytes. The available number range that can be stored in each of these data types depends completely on what range of numbers can be represented by each of these storage allocations.

Table 42.2 The Inventory Table

Column Name Data Type
Prd_ID int
Inv_Count smallint

To wrap up your project initialization, name your project InvMaint and give the project a description to remind yourself what this module does at a later time. You'll also mark this module for unattended execution so that it can run in a multithreaded environment. You have a single class in this project, and you name it InvMnt.


TIP: It a good idea to provide a project description for the modules that you are building in this and the following chapters. You will be building several DLLs and including references to DLLs that you have already built. By including a description, the project description will show up in the Project References dialog box. If you don't provide project descriptions, only the project name will show up in the References dialog box.

Stateless Objects

One key to designing and building well-performing components for use with Transaction Server is to design the objects and methods to be stateless. This means that there are no variables and conditions that are held within the object, all variables are received as parameters to the methods that are exposed, and all results are returned either as the result of the method or through method parameters that were passed by reference.

The primary reason for building stateless objects is because the same method can be called as part of several different applications. These calls can happen simultaneously or sequentially; regardless, it is a high likelihood. Keep in mind that this is the same approach that you need to take when building thread-safe objects. It is possible to build stateful objects for use with Transaction Server, but these objects entail a lot more overhead and will not perform as well when the system is under load.

There are a few exceptions to the rule of building stateless objects. These exceptions consist primarily of information that the object will be using across all processes, such as the database connection information. To build your server object in a stateless manner, your declarations are limited to the database connect string and the error number that you will be raising in the event of an error. This gives us the class declaration section found in Listing 42.1.

Listing 42.1 INVMNT.CLS--Error Number and Database Connection Information

Option Explicit
`We always return the same error number
Private Const ERROR_NUMBER = vbObjectError + 0
`The database connect string
Private Const strConnect = "DSN=InvMntDB;UID=TxsVB;PWD=vbtxs;"

Transaction Context

When an object is running with Transaction Server, it is running in the context of a transaction. A transaction attribute controls how the objects interact with the current transaction within their context. This transaction attribute can have any one of four values, as seen in Table 42.3.

Table 42.3 The Transaction Attribute Values

Value Description
Requires a transaction Objects with this transaction attribute must execute within the scope of a transaction. If the object that called this object was executing within a transaction, this object executes within the scope of the same transaction as the calling object. If the calling object is not executing within a transaction, this object starts (and completes) a new transaction.
Requires a new transaction Objects with this transaction attribute always begin (and complete) a new transaction, whether or not the calling object was running within a transaction.
Supports transactions Objects with this transaction attribute execute within the scope of the transaction of the calling object, if that object was executing within a transaction. If the calling object was not executing within a transaction, this object executes without a transaction.
Does not support transactions Objects with this transaction attribute always execute outside the scope of any transactions, regardless of the transaction state of the calling object.

By using the transaction attribute on all objects running within Transaction Server, you can configure an extensive transaction model, separating objects into distinct transactions that are executed within the midst of other transactions.

For example, take the collection of objects in Figure 42.11. In this model, Object A calls Object B, which calls Object C. All three objects have their transaction attributes set to Requires a transaction. The transaction in which all three objects are executing will commit the changes made by these objects only if all three objects execute successfully. If any one of the three has an error, the entire transaction is rolled back.

FIG. 42.11
The transaction attribute enables you to define separate transactions within other transactions.

Notice that Object D is also called by Object B; only Object D has its transaction attribute set to Does not support transactions. This means that Object D does not affect the transaction within which Objects A, B, and C are executing. Object E, which is called by Object D, might have its transaction attribute set to either Requires a transaction or Requires a new transaction. Because Object D does not support transactions, Object E always starts a new transaction, which is completely independent of the transaction of Objects A, B, and C. If Object E is set to Requires a transaction and Object D is set to Requires a new transaction the transaction of A, B, and C would continue to be independent of the transaction of D and E.

Being able to tell Transaction Server whether an object was successful--or that it ran into problems--requires a few lines of code. The first task is to get a reference to the transaction context object, which is the transaction context within which the object is executing. This is done with the GetObjectContext() function, as in the following code:

Dim ctxObject As ObjectContext
Set ctxObject = GetObjectContext()

After you have the transaction context, you can use the context object's two methods, SetComplete and SetAbort, to tell Transaction Server whether the object was successful or not. If your object executed without any problems, you use the following call to tell Transaction Server that you are finished and that the transaction can be committed:

ctxObject.SetComplete

If your object ran into problems, you can use the SetAbort method to tell Transaction Server that the transaction should be rolled back, as follows:

ctxObject.SetAbort


NOTE: The context object (ctxObject) does more than just control the transaction. It also controls the objects and the resources that those objects are consuming. When an object calls SetComplete or SetAbort, the object is informing Transaction Server that the object is finished processing and can be deactivated, and all the system resources being used by the object can be reallocated to other objects. If SetComplete or SetAbort are not called, Transaction Server does not know when it can release those resources for use by other objects. This eventually bogs down system performance.

You can take this understanding of how to use the context object to build your first object, which will maintain the current inventory of a particular product in the warehouse. Name your method ChangeInv, and pass it a product identification number and the number of the product that you want to move. A positive number adds inventory to the warehouse, and a negative number removes inventory from the warehouse. You also have this object tell how much of the product remain in stock after your request, and (in the case of a negative number) how much needs to be back-ordered to fulfill your request. You can do this with the code in Listing 42.2.

Listing 42.2 INVMNT.CLS--Adding and Removing Inventory from the Warehouse with the ChangeInv Method

Public Function ChangeInv(aiPrdID As Long,aiChange As Integer, _
                                  ByRef aiBackOrder As Integer, _
                       ByRef aiStockRemain As Integer ) As Integer
    Dim ctxObject As ObjectContext
    Dim rdoConn As rdoConnection
    Dim strSQL As String
    Dim rdoRS As rdoResultset
    
    `Get our object context
    Set ctxObject = GetObjectContext()
    
    `Set up error handling
    On Error GoTo ErrorHandler
    
    `Obtain the RDO environment and connection
    Set rdoConn = rdoEngine.rdoEnvironments(0).OpenConnection("", _
                                rdDriverNoPrompt, False, strConnect)
      
    `Update the Inventory
    strSQL = "UPDATE Inventory SET Inv_Count = Inv_Count + " _
              + Str$(aiChange) + " WHERE Prd_ID = " + Str$(aiPrdID)
    rdoConn.Execute strSQL, rdExecDirect
    `Get resulting inventory which may have been further 
    `updated via triggers
    strSQL = "SELECT Inv_Count FROM Inventory WHERE Prd_ID = " _
                                                  + Str$(aiPrdID)
    Set rdoRS = rdoConn.OpenResultset(strSQL, rdOpenForwardOnly, _
                                 rdConcurReadOnly, rdExecDirect)
    `Did we retrieve anything?
    If rdoRS.EOF <> True Then
        `Yes, get the current inventory count
        aiStockRemain = rdoRS.rdoColumns("Inv_Count")
        `Check if the inventory is overdrawn
        If aiChange < 0 And aiStockRemain < 0 Then
            `Set the number of items that are backordered
            aiBackOrder = 0 - aiStockRemain
            `Update the inventory count
            strSQL = "UPDATE Inventory SET Inv_Count = 0 WHERE Prd_ID = " _
                                                 + Str$(aiPrdID)
            rdoConn.Execute strSQL, rdExecDirect
            aiStockRemain = 0
        Else
            `We are not overdrawn, so we don't need to back order anything
            aiBackOrder = 0
        End If
    Else
        `No, there is a problem as no product inventory record was found
        Err.Raise ERROR_NUMBER, "Could not find product inventory record."
    End If
    
    `Close the database connection
    rdoConn.Close
    
    `Tell Transaction Server that we have successfully completed our task
    ctxObject.SetComplete
    
    `Return a 0 to signal that we were successful
    ChangeInv = 0
    
 Exit Function
ErrorHandler:
    `Have we connected to the database yet?
    If Not rdoConn Is Nothing Then
        `If so, then close the connection
        rdoConn.Close
    End If
    
    `Tell Transaction Server that we had problems
    ctxObject.SetAbort
    `Indicate that an error occured
    ChangeInv = -1
End Function

Registering Visual Basic DLLs with Transaction Server

After you have built your Visual Basic project into an ActiveX DLL, you need to register it with Transaction Server before it can be used. You do this through the Transaction Server Explorer. After you have started up the Transaction Server Explorer, you need to make sure that the DTC is running. You can tell if the DTC is running by looking at the color of the screen in the computer icon for your computer. When the DTC is running, the screen on the computer icon is green, and when the DTC is not running, the computer screen is black. If the DTC is not running, click the Computer icon for your computer, and then choose Tools, MS DTC, Start.

Creating Packages Before you can start registering components in Transaction Server, you must have a package into which you are going to install the components. Packages are logical groupings of objects that are generally used as a unit. As a general rule, you will want to create one package for every set of applications that uses Transaction Server. You can create a package by following these steps:

1. Select the Packages Installed folder.

2. Choose File, New from the main menu.

3. On the first screen of the Package Wizard, choose Create an Empty Package, as seen in Figure 42.12.

FIG. 42.12
For registering com- ponents that you have built, you need to create an empty package into which the components will be installed.

4. Type a name for the package, as shown in Figure 42.13. For your catalog sales company package, call it Inventory. Then click the Next button.

FIG. 42.13
Provide the package with a name that reflects the functionality, or family of applications, that the components in the package will be providing.

5. If the objects in the package need to run under a specific login account, select the This User radio button and provide the user name and password. Otherwise, leave the default radio button selected, as in Figure 42.14, which runs all the objects in the package under the account of the users using the applications that use the objects in this package. (This can affect the availability of resources for the process components, depending on how the access privileges are configured in the system security.)

FIG. 42.14
If you need the components in the package to execute as a specific user login, for resource access purposes you need to specify the user login and password.

6. Click the Finish button to complete the process.

Installing Components After you have a package, you can begin installing components into it. This is where you register the ActiveX DLLs that you have and will be creating with Visual Basic. You can register your components by following these steps:


NOTE: In the following section, the terms install and register are used interchangeably. Installing components into Transaction Server and registering components with Transaction Server are two ways of referring to the same process.
1. Select the Components folder in the package into which you want to install the components, as seen in Figure 42.15.

2. Click the Install New Component(s) button, as seen in Figure 42.16.

3. Click the Add Files button and select the ActiveX DLL that you are wanting to register, as seen in Figure 42.17.

4. When you return to the Install Components dialog box, the upper list box should show the DLL that you are installing, and the lower list box should show all the visible classes within the DLL, as in Figure 42.18.

FIG. 42.15
Select the Components folder in the package that you have created to install the newly created Inventory package.

FIG. 42.16
If you are installing components that you have built, you need to select the Install New Components option.

FIG. 42.17
You need to select the DLL containing the components that you are installing.

FIG. 42.18
The Install Components dialog box displays all the components found in the specified DLL.

5. Click the Finish button, and the components are installed in Transaction Server, as seen in Figure 42.19.

FIG. 42.19
After you install the components, they show up in the Transaction Server Explorer.

6. Select the components you just installed, one at a time, and right-click the mouse. Select Properties from the pop-up menu. On the component Properties Editor, select the Transaction tab, and select the transaction attribute desired for the currently selected component, as in Figure 42.20.

Whenever you recompile any ActiveX DLL built in Visual Basic, you need to refresh the component information in Transaction Server. This can be easily done by deleting the component from the Transaction Server Explorer and reinstalling the component by following the same steps as were followed to install the component originally. Another way of refreshing the component information in Transaction Server is by choosing Tools, Refresh All Components.

FIG. 42.20
You need to open the properties dialog box for the installed component to specify the transaction attribute setting.


A Note from the Authors
In working with Transaction Server 1.0, the Refresh All Components menu option often scrambles the component names in the package that we were working with. We end up with what looks like two or three copies of the same component in the package, and some components appear to be missing. We do not notice any problems when attempting to run the applications that used these components, but most often end up deleting all the components from the package and reinstalling them. Hopefully, this behavior will be corrected in an upcoming service pack.

Calling Transaction Server Objects from Visual Basic

Now that you have a component built and registered with Transaction Server, how do you call the method in this object from a Visual Basic application? First, the Visual Basic application has to get a reference to the Transaction Server object. After the reference has been acquired, the object's methods can be called. A reference to a Transaction Server object can be acquired in three ways:


NOTE: A fourth method for acquiring a reference to a Transaction Server object by another Transaction Server object is through the use of the Context Object's CreateInstance method, which creates the object reference in the same transaction context of the current object (depending on the new object's transaction attribute). You see this method in use in the following chapters.

From a Visual Basic application, you can create your reference to your Inventory Maintenance object with the following code:

Dim obj As Object
Set obj = CreateObject("InvMaint.InvMnt")


NOTE: The same Transaction Server object could have been created with the New keyword by using the following code:



Dim obj As New InvMaint.InvMnt




As a general rule, all Transaction Server components can be referenced in the same way as all other ActiveX server objects. Transaction Server works with the operating system to make sure that when Transaction Server components are requested, they are created and called within Transaction Server.


From here, you can call the object methods by referencing them via the object you have just created, as so:

obj.ChangeInv(iiProductID, CLng(txtCount.Text), iBackOrder, iCurInventory)

If you build a simple little applet by using the Remote Data Control and the Data Bound Combo Box, you can call your object and see how Transaction Server works. The first thing you need to do is to add the Remote Data Control and the Data Bound List Controls to your Toolbox. You do this by choosing Project, Components, as seen in Figure 42.21.

FIG. 42.21
You have to include the Data Bound List Controls and the Remote Data Control into your Visual Basic project before you can use them.

With these two controls, you can build a simple form that provides a drop-down list box, containing the product descriptions from the Products table in the database. You accomplish this by binding the Remote Data Control to the ODBC configuration you have set up for your database, providing the user name and password that you have configured, and using the following SQL to populate the Remote Data Control:

SELECT * FROM Products

Next, specify the Remote Data Control as the row source for the Data Bound Combo Box and specify the Prd_Desc column as the ListField, DataField, and BoundColumn. You add a text box for the user to enter the number to add or remove from the inventory, and you have the form seen in Figure 42.22. The complete source code for this form can be found on the CD.

FIG. 42.22
Use a very simple form to call the method in the object you registered with Transaction Server.

Setting the Product ID

When the user selects a product, you need to have a variable into which to place the selected product ID, so you declare a variable in the form declarations by using the code in Listing 42.3.

Listing 42.3 RECVNG.FRM--Declaring a Variable for Holding the Selected Product ID

Option Explicit
`We always return the same error number
Private Const ERROR_NUMBER = vbObjectError + 0
`The currently selected product ID
Dim iiProductID As Long

You set the product ID into this variable whenever the user selects a product from the list by navigating in the Remote Data Control to the currently selected row, and then getting the Prd_ID column from that row, as in Listing 42.4.

Listing 42.4 RECVNG.FRM--Grabbing the Product ID from the Remote Data Control When the User Selects a Product

Private Sub dbcProduct_Click(Area As Integer)
    Dim varCurRecord As Variant
    
    `What is the currently selected item?
    varCurRecord = dbcProduct.SelectedItem
    `Move to the selected record in the result set
    MSRDC1.Resultset.Move 0, varCurRecord
    `Grab the product ID
    iiProductID = MSRDC1.Resultset!Prd_ID.Value
End Sub


CAUTION: The code that you are using to grab the selected product ID is dependent on the Remote Data Control being used for the ListField, DataField, and the BoundColumn. If you use the Remote Data Control for just the ListField, the dbcProduct_Click method will error out with an invalid bookmark error when you first click it to select a product. This is due to the fact that the control starts out with the selected item of 0, which the Remote Data Control interprets as an invalid row. It is after you have selected a row that the control has a valid row number, but you cannot get to that point.


NOTE: You could easily have waited until the user clicked the Received button when calling the Transaction Server object to determine the selected item. Calling the object in this way requires additional code in the specific method isolated by using the method that you have. This is not really better, but it does isolate the functionality, allowing you to focus on the core functionality that you are implementing in each method.

Calling the Transaction Server Object

When the user clicks the Received button, you can create a reference to the Transaction Server object that you created by using the earlier code snippets, and you can display for the user the resulting stock and back-order amounts with the code in Listing 42.5.

Listing 42.5 RECVNG.FRM--Creating a Reference to Your Transaction Server Object and Calling the Method That You Created in the Object

Private Sub cmdReceived_Click()
    Dim iBackOrder As Integer
    Dim iCurInventory As Integer
    Dim ProgID As String
    Dim obj As Object
    
    `Set up the error handling
    On Error GoTo ErrorHandler
    
    `Decide which component to use
    ProgID = "InvMaint.InvMnt"
    
    `Create the appropriate object
    Set obj = CreateObject(ProgID)
    `Were we able to create the object?
    If obj Is Nothing Then
        MsgBox "Create object " + ProgID + "failed."
        Exit Sub
    End If
    `Call the object method
    If obj.ChangeInv(iiProductID, CLng(txtCount.Text), iBackOrder, _
                                iCurInventory) = -1 Then
        Err.Raise ERROR_NUMBER
    End If
    
    `Release the object
    Set obj = Nothing
  
    `Display for the user what the current inventory is
    MsgBox "New Inventory received, current backorder count = " _
              + Str$(iBackOrder) + " and current inventory = " _
              + Str$(iCurInventory)
    Exit Sub
    
ErrorHandler:
    `Show the user the error message
    MsgBox "Error " + Str$(Err.Number) + " : " + Err.Description
    Exit Sub
End Sub

Before you run your form, let's change the view in the Transaction Server Explorer to show the status of your object. You do this by selecting the Components folder in the package you have created. Next, choose View, Status. The right side of the Explorer now shows the activity status of your object. If you run the form with the Transaction Server Explorer where it can be seen, you can watch as the object that you created earlier is instantiated and executed, as seen in Figure 42.23.

FIG. 42.23
When the form that you have created calls the object that you regis- tered in Transaction Server, you can watch as the object is created and run.

From Here...

In this chapter, you learned about the different types of middleware, and how they enable client/server applications to be scaled for use by many more users than traditional client/server applications. You saw how Microsoft's Transaction Server is somewhat of a cross between a Transaction Monitor and an Object Request Broker, providing all the functionality of the first with a substantial amount of the functionality of the latter. Later, you saw how you can build objects with Visual Basic that can be registered with Transaction Server for use by applications running on client systems. Finally, you saw how you can build a front-end application that uses the object that you built and loaded into Transaction Server.

From here, you might want to check out the following chapters:


Previous chapterNext chapterContents


Macmillan Computer Publishing USA

© Copyright, Macmillan Computer Publishing. All rights reserved.