Visual Basic 6 Business Objects
ADO 2.0 provides us with some powerful new capabilities that we can use to help us serialize an objects data and transfer it from one process to another or across the network from one computer to another. The core of this capability lies with ADOs support for batch mode updating of a Recordset object. With this capability, ADO is able to not only provide a reference to a Recordset object, but it can actually copy the objects data from one process to another or from one machine to another essentially allowing us to pass ADO Recordset objects by value rather than by reference.
Couple the ability to move Recordset objects across the network with ADO 2.0s support for Recordset objects that are disconnected from any database connection. This means we can create a Recordset object out of thin air no database required. We can define the columns of data we want to provide, then add or manipulate rows of data at will.
Between these two capabilities, we can use ADO 2.0 to create an arbitrary Recordset object to store any data wed like and then pass that Recordset from process to process or machine to machine as we choose. ADO takes care of all the details of serializing the Recordset itself, allowing us to simply interact with a Recordset object to view, update or add our data.
Before we can pass a Recordset around our network we need to create it. There are two ways to create a Recordset object for serializing our objects data creating the Recordset from a database, or creating a connectionless Recordset through code.
Creating Recordset Objects from Data
The most common way to create a Recordset object is to select some data from a database to be loaded into the object. However, if were going to pass that Recordset around the network we do need to take some extra steps as we open it.
In particular, we need to set the CursorLocation property of our Connection or Recordset object must be set to adUseClient. This causes ADO to use a cursor engine located on the client machine rather than one within the database server itself. Since the cursor engine is local to the client, we can send the Recordsets data to any machine where ADO or ADOR (the light-weight client version of ADO) is installed.
We also need to specify the LockType property of our Recordset as adLockBatchOptimistic. This causes ADO to build our Recordset object in a batch update mode, allowing us to manipulate the Recordset and its data even if it is not currently connected to the data source.
By setting these two properties as we initialize our Recordset object we will cause ADO to automatically support batch processing of our data, and to automatically pass the Recordset objects data to any process that interacts with the object.
There is one caveat to this approach. Our CursorType property can only be one of adOpenKeyset or adOpenStatic when we are using a batch mode Recordset. If we are passing the Recordset to a machine that only has ADOR installed (such as a thin client workstation), then we can only use the adOpenStatic setting for the CursorType property.
Lets take a look at some code that opens a Recordset and returns the object upon request:
Public Function GetData() As Recordset Dim rs As Recordset Dim strConnect As String Set rs = New Recordset strConnect = "Provider=Microsoft.Jet.OLEDB.3.51;" & _ "Persist Security Info=False;Data Source=D:vb6video.mdb" With rs .CursorLocation = adUseClientBatch .Open "select * from customer", strConnect, adOpenStatic, _ adLockBatchOptimistic End With Set GetData = rs Set rs = Nothing End Function
This code doesnt look much different than what wed expect to see any time we open a Recordset based on some data in a database. However, there are a couple interesting things to note. First off, before calling the Open method we set the CursorLocation property of the object:
.CursorLocation = adUseClientBatch
Then, in the call to the Open method we set the LockType to adLockBatchOptimistic. We also set the CursorType to adOpenStatic. We could have set it to adOpenKeyset if wed chosen, but by choosing adOpenStatic we know we can pass the Recordset to a client that might only have ADOR installed without having ADO convert our cursor type during that process
Creating a Connectionless Recordset
While we can create Recordset objects from a database, that often wont work well for serializing data from our objects. It is not at all unusual for an objects state to include information that isnt necessarily stored in a database. For instance, we may wish to pass a flag indicating whether our object is new or other types of information between our client workstation and the application server. If our Recordset object is generated directly from a database query we are restricted to only passing information that comes from that database.
Fortunately ADO 2.0 provides a very elegant solution to this problem by allowing us to create a Recordset object that is totally unrelated to any data source. The steps involved in this process are quite straightforward:
- Create the Recordset object
- Add columns to the Recordset using the Fields objects Append method
- Open the Recordset
- Add or manipulate data in the Recordset
As an example, the following code creates a connectionless Recordset with two columns of data: Name and BirthDate. We then add a couple rows of information to the Recordset object and return it as a result of the method:
Public Function MakeData() As Recordset Dim rs As Recordset Set rs = New Recordset With rs .Fields.Append "Name", adBSTR .Fields.Append "BirthDate", adDate .Open .AddNew .Fields("Name") = "Fred" .Fields("BirthDate") = "1/1/88" .Update .AddNew .Fields("Name") = "Mary" .Fields("BirthDate") = "3/10/68" .Update End With Set MakeData = rs Set rs = Nothing End Function
The first couple lines simply declare and create an instance of a Recordset object. Once thats done we can move on to adding columns to the empty Recordset by calling the Append method of the Fields object:
.Fields.Append "Name", adBSTR .Fields.Append "BirthDate", adDateAfter we have columns defined, all that remains is to load our object with some data. This is as simple as calling the AddNew method, loading some data and calling Update to store the data into the Recordset. Of course the data isnt stored into any database, since this Recordset exists only in our computers memory.
Passing a Recordset by Value
Weve now looked at two different ways to create a Recordset object that we can pass to another process or across the network. Both of the routines shown above are written as Function methods, returning the Recordset object as a result.
ADO itself handles all the details of moving the Recordset objects data to the client process or computer, so we really dont need to do any extra work at all beyond setting the properties as we did to create the Recordset. Our client code can be quite simple as shown by the following fragment:
Dim objServer As Object Dim rs As Recordset Set objServer = CreateObject("MyDataServer.DataMaker") Set rs = objServer.GetData Set objServer = Nothing
Once this code fragment is complete, we have a Recordset object to work with. This code assumes that the code to create the Recordset is in an ActiveX server named MyDataServer and in a class named DataMaker. This ActiveX server could be running in another process, or on another machine on the network.
Regardless, once weve got the Recordset through this code, the MyDataServer ActiveX server can be totally shut down the machine it is running on could even be shut off and our code can still continue to work with the Recordset and its data.
The program running this code fragment does require a reference to either the ADO or ADOR library in order to function. In many cases the lighter-weight ADOR library is sufficient, as it provides basic support for interacting with Recordset objects that are created and updated by another process or machine.
A property bag is an object that supports the concept of a key-value pair. The idea is that the property bag can store a value associated with a key, or name, for that value. For instance, we might store the value 5 along with the key Height. At any point we can also retrieve the Height value from the property bag.
Visual Basic 5.0 introduced the concept of a PropertyBag object as part of the ability to create ActiveX controls. While the concept was useful when storing properties of our control that the developer set at design time, we couldnt take advantage of the PropertyBag object outside of control creation.
Visual Basic 6.0 extends the PropertyBag object such that we can use it anywhere we choose within our applications. Basically, anywhere that we need to manage key-value pairs, we can make use of the PropertyBag object provided by Microsoft.
Better still, the PropertyBag object implements a Contents property that allows us to access the entire contents of the object as a single Byte array essentially it provides built-in support for streaming its own data. We can retrieve the data, send the Byte array as a parameter to another process or across the network, and then place it into a PropertyBag object, giving us an exact duplicate of the object we started with.
Of course Byte arrays arent nearly as easy to manipulate or work with as a String variable would be. Fortunately this isnt a serious problem either, as Visual Basic makes it very easy for us to convert a Byte array to a String and then back to a Byte array.
Serializing an Objects Data
Lets take a look at some simple code that illustrates how we can use a PropertyBag to serialize the data in an object.
Suppose weve got an object with two pieces of data: Name and BirthDate. We can store this data in a PropertyBag object with code similar to this:
Public Function GetObjectData() As String Dim pbData As PropertyBag Set pbData = New PropertyBag With pbData .WriteProperty "Name", mstrName .WriteProperty "BirthDate", mdtBirthDate End With End Function
Once weve created the PropertyBag object, we can simply use its WriteProperty method to store the values from our object into the property bag. In this case, were assuming that the name and birth date data are stored in the variables mstrName and mdtBirthDate.
After our property bag has our objects data, we can retrieve all the data in a single Byte array using the PropertyBag objects Contents property.
Public Function GetObjectData() As String Dim pbData As PropertyBag Set pbData = New PropertyBag With pbData .WriteProperty "Name", "Fred" .WriteProperty "BirthDate", "5/14/77" End With GetObjectData = pbData.Contents End Function
With this simple code weve converted our objects data into a single String variable that we can easily pass as a parameter to another object, even across the network.
Deserializing an Objects Data
Now that weve seen how we can take data from an object and use a PropertyBag to serialize that data into a simple String variable, lets take a look at how we can use that String variable to load another object with the data.
Since we know well be receiving a String value, the first step is to convert that String into a Byte array so we can place it into the PropertyBag objects Contents property. While were doing this, well also need to create a PropertyBag object to work with:
Public Sub LoadObject(StringBuffer As String) Dim arData() As Byte Dim pbData As PropertyBag Set pbData = New PropertyBag arData = StringBuffer pbData.Contents = arData End Sub
Once weve converted the String to a Byte array, we simply set the Contents property of our PropertyBag using that value. This causes the PropertyBag object to contain the exact data that was contained in the other PropertyBag object that we used to create the String variable.
Now that the PropertyBag object has been populated we can use the ReadProperty method to retrieve the individual data values for use by our object:
Public Sub LoadObject(StringBuffer As String) Dim arData() As Byte Dim pbData As PropertyBag Set pbData = New PropertyBag arData = StringBuffer pbData.Contents = arData With pbData mstrName = .ReadProperty("Name") mdtName = .ReadProperty("BirthDate") End With End Sub
In many ways the use of a PropertyBag object for serializing our objects data is comparable to how we used the LSet command to convert a UDT to a String. Either approach results in our objects data being converted into a single String value that we can pass as a parameter, store in a database or send as the body of an email or Microsoft Message Queue (MSMQ) message. Once the data reaches the other end of its journey we can easily reconstitute our object by converting the String value back into its original form.
Page 5 of 13