Browse DevX
Sign up for e-mail newsletters from DevX


A Design Pattern for Creating Reusable COM+ components using Visual Basic and XML : Page 3

Over the last year or two, quite a few articles and books have been written on COM+, transaction management, and programming stateful & stateless components, explaining in detail what COM+ does and how it does it. When tasked with creating a model for developing lightweight, reusable components, you can found a lot of What-not-How type of articles on COM+, but not a lot of practical How-To type of articles. The author started working through a lot of information on COM+, combined this information with how I wanted a reusable component to behave, and came up with a real easy model for creating reusable COM+ business objects.


How the CO object uses the BO object

If you look at the code for the f_Load method of clsClientSVR in our sample project  (shortened code shown below), you will see that on BO level we have f_Load and LoadLoad does the actual work, while f_Load just serves as a wrapper providing clsClient with access to this logic.

  Public Function f_Load(ByVal pstrConnection$, ByVal plngID As Long, ByRef pstrXML$) As Boolean
      f_Load = Load(pstrConnection, plngID)
      ptrXML = Pack
      End Function
      Public Function Load(ByVal pstrConnection$, ByVal plngID As Long) As Boolean
      Dim lobjSQLRead As ClientBO.clsSQLRead
      Dim lobjRS As ADODB.Recordset
      Dim lstrSQL$

      'The work happens here : Perform a load from database into objects private variables 
      Load = True
  End Function

When an external application uses ClientCO (code shown below) to load an object from the database, the following logic is executed:

  1. clsClient.Load instantiates an instance of the BO object and calls the f_Load function of this object
    1. On the BO, f_Load calls the method Load
      1. Load loads the information from the database into its private state variables
    2. f_Load packs the object instance state into an XML string
  2. The instance state is unpacked from the XML string into the CO object's local state variables

      Public Function
      Load(ByVal plngID As Long) As Boolean
      Dim lobjBO As ClientBO.clsClientSVR
      Dim lstrXML$
      Set lobjBO = New ClientBO.clsClientSVR
      Load = lobjBO.Load(Me.System.ConnectionString, plngID, lstrXML)
      Set lobjBO = Nothing
      UnPack lstrXML
      End Function


How another BO object uses this BO object

Using the object on BO layer is pretty simple if you keep the COM+ Rules in mind.  The code snippet below shows how another COM+ object (let’s call it Object A) would use the functionality offered by clsClientSVR

      Public Function Validate_Client(pstrConnection as String)
      Dim lobjClient As ClientBO.clsClientSVR
      Dim lstrName as String
      Dim lobjMTS As ObjectContext: Set lobjMTS = GetObjectContext()
      Set lobjClient = lobjMTS.CreateInstance(ClientBO.clsClientSVR)
        lobjClient.Load pstrConnection, 1234
        lstrName =lobjClient.Name
      Set lobjClient = Nothing
      End Function

In the above code, the root object passes the connection string to the secondary object.  This means that my sample will only work if the tables for these two objects are located on the same database.  In the real world you may find that your objects may be residing on different databases and even different machines, so your calling application will have to allow for more than one set of configuration data to be carried.

The programmer of Object A needs to take into account the transaction settings of both Object A and ClientBO.clsClientSVR (lets call it Object B) when implementing such functionality.  The programmer’s options are not limited by the transactional settings of Object B, even if he receives it in binary format, as the MTSTransactionMode setting can be overridden by setting the property on the COM+ administration console (there are some limitations though, for example: You cannot trust an object originally marked Not an COM+ Object object to participate in a transaction if was not designed for that purpose).  The programmer will have to decide whether Object A and Object B should share the same transaction, participate in separate transactions, or if one or either should run without transactions. 

It is important to note that Object B will join Object A in its transaction only if Object A is already running within a transaction (MTSTransactionMode = Requires Transaction  or Requires New Transaction) and Object B has MTSTransactionMode setting of Uses Transaction  or Requires Transaction

As can be seen from above code, using Client on a BO level is pure object oriented programming.  Transaction and state management is done by COM+.  The root object trusts the secondary object not to call SetComplete between method calls.  The developer of the root object can easily obtain the MTSTransactionMoce setting of the secondary object by using the COM+ administration console.  The developer can then use the COM+ State Rules to decide whether the two objects can use the same transaction and what the scope of the second object should be. If the root object runs inside a different transaction than the secondary object, the root object can control the scope of the transaction of the secondary object by either calling the SetComplete method of the secondary object by means of a wrapper method, or by simply deactivating the object as soon as possible.  I prefer the second option, as it fits neatly into the rule Acquire resources late and release them early.  For interest’s sake, the SetComplete wrapper method can be implemented as shown below.  If Object A calls this method on Object B, when the method completes it passes through the object context when returning control to Object A, at which time COM+ will inspect the IsDone flag and recycle Object B.

      Public Sub SetComplete()
      Dim lobjMTS As ObjectContext: Set lobjMTS = GetObjectContext()
         If Not lobjMTS  Is Nothing Then lobjMTS.SetComplete
      End Function

In the samples I always use the CreateInstance method to create a new object instance.  In real life however, I prefer to wrap this inside a function. This is very useful when switching between MTS and COM+, as you should use CreateInstance for MTS while CreateObject is the preferred method for COM+.  The function is quite simple and does the following (sample shown is configured for use with MTS - in COM+ I always use CreateObject

      Public Function Generate_Object(pstrProgID$) As Object
      Dim lobjMTS As ObjectContext: Set lobjMTS = GetObjectContext
      If lobjMTS Is Nothing Then
         Set Generate_Object = CreateObject(pstrProgID)
         Set Generate_Object = lobjMTS.CreateInstance(pstrProgID)
       End If
      End Function

Thanks for your registration, follow us on our social networks to keep up-to-date