MORE INFORMATION
How It Works
What goes on when a call is made to a queued component, and an
object is passed as an argument?
Here's an example: Suppose you have
an object you use to manage customer information called Customer.CCustomer.
This object is implemented in the Customer.dll file and has the following code.
Note that it is marked as persistable, and note its implementation of the
appropriate methods:
'CCustomer Class. Marked as 'Persistable'
Option Explicit
Public Name As String
Public Age As Integer
Public Address As String
Private Sub Class_ReadProperties(PropBag As PropertyBag)
Name = PropBag.ReadProperty("Name")
Age = PropBag.ReadProperty("Age")
Address = PropBag.ReadProperty("Address")
MsgBox "Person read!!"
End Sub
Private Sub Class_WriteProperties(PropBag As PropertyBag)
PropBag.WriteProperty "Name", Name
PropBag.WriteProperty "Age", Age
PropBag.WriteProperty "Address", Address
MsgBox "Person Written!!"
End Sub
Private Sub Class_Initialize()
MsgBox "CCustomer Class_Initialize"
End Sub
Private Sub Class_Terminate()
MsgBox "CCustomer Class_Terminate"
End Sub
This sample class does nothing except cache information. In a real
world scenario, however, you would probably have other methods (and no message
boxes in Class_Initialize!).
Now imagine that there is an object that
is designed for server use. This object is called Orders.COrder, is in a
separate file called Orders.dll, and has a function with the following code:
Public Sub AddOrder(ByVal ItemID As Long, ByVal Amount As Double, ByVal Customer As Customer.CCustomer)
MsgBox "Customer order for item '" & ItemID & "' for " & Customer.Name
End Sub
Note that the customer information for the order is directly specified
as an object of type Customer.CCustomer. In the real world, you would probably
engage in some database activity in this method. However, in this sample all
you do is raise a message box.
And last but not least, there is the
client EXE application. All this has is a form and a command button in which
you type the following code:
Dim oOrder As COrder
Dim oCust As CCustomer
Set oCust = New CCustomer
oCust.Name = "Eduardo A. Jezierski"
oCust.Age = 23
oCust.Address = "One Microsoft Way, Redmond, WA"
Set oOrder = New COrder
oOrder.AddOrder 123, 100, oCust
Set oOrder = Nothing
Set oCust = Nothing
Say you have configured your orders component inside of COM+, adding it
to a COM+ application. When a user clicks the button on the client, the
following happens:
- A CCustomer object is created (raising the CCustomer Class_Initialize message box) and populated by the client.
- A COrder object is created.
- The AddOrder method is executed in the COM+
application.
- A message box saying "Customer order for item '123' for
Eduardo A. Jezierski" appears.
- The COrder instance is destroyed.
- The CCustomer instance is destroyed, bringing up the CCustomer Class_Terminate message box.
Bring in the Queues
So what happens now if you bring on Queued Components?
To test this, first configure your COrder component as queued. See the
following Microsoft Knowledge Base article for instructions:
246825 How To Configuring a COM+ application for Queued Components
Next, modify the client application so that the
components created are queued. To do so, change the line where the COrder is
created to be as follows:
Set o = GetObject("queue:/new:Orders.COrder")
This will effectively give you a "stand-in" COrder object that will
record the method calls and will dispatch them to the COrder object sitting in
your COM+ application.
The rest of the application remains the same.
When you test it, however, a very different set of events occurs:
- First, a customer object is created and populated by the
client.
- A COrder queued recorder instance is created using the
GetObject function with the specified moniker.
- The AddOrder method is invoked. The QC recorder, behaving
as a COrder, takes in the Customer object, which gets its Class_WriteProperties
function called so that its state is persisted, together with all other
parameters (ItemID and Amount).
- The COrder-recorder instance is set to nothing. At this
point, all the calls invoked on it (just the AddOrder in this case) are packed
together with the client security token and other information, wrapped in a
Microsoft Message Queue Server (MSMQ) message, and sent off to the
destination.
- Having finished its job, the client destroys the customer
instance and finishes its work.
Meanwhile...
- Once the message is sent, you assume that it eventually
reaches the intended application's queue.
- Because the application was set to "listen" for messages,
it detects the MSMQ message arrival and figures out that it belongs to a COrder
class used by user X. After doing access checks, it creates a COrder instance on the
server.
- Now the "playback" part of the story begins. The QC player
starts invoking the methods the recorder stored in the message.
- In this case, that is just the AddOrder method. However,
because the parameters include a Customer object, first a CCustomer instance is
created, and it is directed to "load" itself through the Class_ReadProperties
function.
- Once the instance is loaded, the player actually invokes
the AddOrder method on the real COrder object, so that it does its chores. It
passes the new Customer instance as the call argument, and once the call is
done, it destroys this temporary instance. It then invokes any other recorded
calls in a similar fashion, and then releases the COrder object and temporary
CCustomer instances.
Your COrder code executed without realizing that the customer
instance it was using was really a local and different instance of the customer
object that stored itself in the client, maybe minutes or hours ago. In this
way, the whole asynchronous model is maintained.