Browse DevX
Sign up for e-mail newsletters from DevX


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

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.


Starting to benefit from reuse

Let us now look at a slightly more complex sample of reuse. In our Insurance project the contract can have multiple clients associated with it.  We want to reuse our Client component to provide a collection of Client objects, and these objects must be accessible on both the BO and CO layers of our Insurance project.  The CO level of the insurance object will reuse our clsClient object, while the BO level of the insurance object will reuse clsClientSVR.  Thus the ContractBO project has to reference the ClientBO project, and the ContractCO project has to reference the ClientCO project.  Note that the only CO project to reference ClientBO is ClientCO.

The relationship between Contract and Client is many-to-many.  I implement this link using a table called ContractClients.  I do not use a compound key for this table, as updating changes to a table with compound keys can become a total nightmare if any one of the key fields changes.  I implement clsContractClient to map to a row in this table, and the collection class colContractClients that will Load all the ContractClient records for a specified contract.  Depending on your business logic, you may decide to Load the corresponding Client object either on first reference, or every time clsContractClient is loaded.  I chose to Load the corresponding Client object in clsContractClient.Load,

The collection class colContractClients takes as parameter the primary key of the contract, and then fetch the primary keys of all the clients associated with this contract, loads these clsContractClient objects and then adds them to the collection.  The advantage of this approach is total encapsulation of my Client object.  There is only one method to load my client object and one method to save it, meaning fixing an error or adding functionality to my Client object has to be done only in clsClientSVR.  The disadvantage of this approach is that, in order to load n clients I will always have n+1 queries to SQL.  In most cases it is possible for the collection class to select all the necessary information in one query and to push this information into the client objects, but that means you need to know exactly how clsClientSVR expects its data, which is not possible if you are aiming for binary reuse.  It boils down to a tradeoff between speed and maintainability/reusability, and I chose the easier maintainable option.  The code for f_LoadAll and LoadAll of colContractClientsSVR is shown below.  As is the case with all other code, f_LoadAll is for use by the CO level object while LoadAll performs the actual work and can be used by other BO level objects.

      Public Function f_LoadAll(ByVal pstrConnection$, ByVal plngContractID As Long, ByRef pstrXML$) As Boolean
      Dim lobj As ContractBO.clsClientSVR
      f_LoadAll = LoadAll (pstrConnection, plngContractID)
      pstrXML = Pack
      End Function
      Public Function LoadAllObjects(ByVal pstrConnection$, ByVal plngContractID As Long) As Boolean
      Dim lstrSQL$
      Dim lobjClient As ClientBO.clsClientSVR
      Dim lobjSQLRead As ContractBO.clsSQLRead
      Dim lobjRS As ADODB.Recordset
      Dim lobjMTS as ObjectContext: Set lobjMTS = GetObjectContext()
      Set lobjSQLRead = New ContractBO.clsSQLRead
      lstrSQL = "Select ClientID from ContractClients where ContractID = " & plngContractID
      lobjSQLRead.ExecuteRS pstrConnection, lstrSQL, lobjRS
      Set mobjColl = New Collection
      While Not lobjRS.EOF
         Set lobjClient = lobjMTS.CreateInstance(ClientBO.clsClientSVR)
         If lobjClient.Load(pstrConnection,(lobjRS!ClientID)) Then
           Add lobjClient
         End If
      Set lobjRS = Nothing
      Set lobjSQLRead = Nothing
      End Function  

The collection class colContractClients provides you with two functions to retrieve objects from the collection, either by primary key (ItemByKey) or by position (ItemByPosition) It also provides an enumerator function. In order to be able to enumerate the collection, you have to set the Procedure ID of the NewEnum function to 4. To set this property, load the project in VB, go to the object browser (F2) and select the project ClientBO. It will then list all your classes in the left pane. Select colContractClientsSVR, and all properties and methods will appear in the right pane. Select NewEnum, right-click and select "Properties" from the pop-up menu. Click on the "Advanced" button, and set "Procedure ID" to 4. As a neat touch, I also like to hide the NewEnum function, so click on "Hide this member" if you share my sentiments. Repeat above steps for colContractClients on the CO layer.

Loading our Contract now becomes a slightly more complex affair, and it is done in 4 steps.

  1. clsContractSVR.Load selects the contract data and loads it into the private variables of clsContractSVR
  2. The LoadAll method of colContractClientsSVR is called. This method selects the primary key field of every clsContractClientSVR object in the collection, and then calls the Load method for each of these.
  3. clsContractClientSVR.Load loads its data into the private variables and then calls the Load method on its private instance of clsClientSVR
  4. clsClientSVR.Load loads the client data into the object

Although the above forms a long chain, it is pretty straightforward OOP programming. We now have the ability to load a rather complex object hierarchy in memory on our COM+ machine. The next step is to transport this hierarchy to the CO level.

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