Programmer to ProgrammerTM  
Wrox Press Ltd  
   
  Search ASPToday Living Book ASPToday Living Book
Index Full Text
 
ASPToday Home
 
 
Bullet HOME
Today TODAY
Index INDEX
Full-text search SEARCH
Reference REFERENCE
Feedback FEEDBACK
Advertise with us ADVERTISE
Subscribe SUBSCRIBE
Bullet LOG IN
                         
 
 
  Register for FREE Daily Updates:  
  Email:  
 
  The ASPToday Article
February 16, 2001
      Previous article -
February 15, 2001
   
 
   
   
   
Combining COM+ Transactions and Message Queuing   Jason Wells  
by Jason Wells
 
CATEGORIES:  Components, Site Design  
ARTICLE TYPE: In-Depth Reader Comments
   
    ABSTRACT  
 
Article Rating
 
   Useful
  
   Innovative
  
   Informative
  
 13 responses

In this article we see how to create a Visual Basic COM+ component that uses transactions to transfer data back and forth between databases and message queues. By using transactions with message queues, we can roll back actions such as sending or receiving messages. This allows us to move data out of a database and into a message queue, or vice versa, while guaranteeing that both operations will succeed or fail together, so that the data can't be lost in transit. Jason Wells shows us how it is done.

   
                   
    Article Discussion   Rate this article   Related Links   Index Entries  
   
 
    ARTICLE

This article will explain how to create a Visual Basic COM+ component that uses transactions to transfer data back and forth between databases and message queues. By using transactions with message queues, we can roll back actions such as sending or receiving messages. This allows us to move data out of a database and into a message queue, or vice versa, while guaranteeing that both operations will succeed or fail together, so that the data can't be lost in transit.

Remember that you should use SQL Server transactions rather than COM+ transactions wherever possible, as they create fewer overheads. Here, because we'll be working with multiple data sources (a database and a message queue), our use of COM+ transactions is necessary.

Before running the code in this article, please install Windows 2000 Service Pack 1, available at http://www.microsoft.com/windows2000/downloads/recommended/SP1/?WROXEMPTOKEN=621086Z65JjQqk8cXs0tH03WEy. Service Pack 1 fixes a bug in COM+ 1.0 that prevents Visual Basic COM+ components from returning extended error information. For more information on this bug, see http://support.microsoft.com/support/kb/articles/Q255/7/35.ASP?WROXEMPTOKEN=621086Z65JjQqk8cXs0tH03WEy.

The Books Table

First we'll create a database table to store data about books. Later, we'll see how to package this data as XML and transfer it back and forth between the database and a message queue. As you can see, the table is simple; for each book, we store a unique ID, the title, and the author:

CREATE TABLE [dbo].[tblBooks] (
   [Id] [int] IDENTITY (1, 1) NOT NULL ,
   [strTitle] [varchar] (50) NULL ,
   [strAuthor] [varchar] (50) NULL 
) ON [PRIMARY]
GO

ALTER TABLE [dbo].[tblBooks] WITH NOCHECK ADD 
CONSTRAINT [PK_tblBooks] PRIMARY KEY  NONCLUSTERED 
   (
      [Id]
   )  ON [PRIMARY] 
GO

INSERT INTO tblBooks (strTitle, strAuthor)
VALUES ('Effective C++', 'Scott Meyers')
INSERT INTO tblBooks (strTitle, strAuthor)
VALUES ('Inside ATL', 'Shepherd & King')
INSERT INTO tblBooks (strTitle, strAuthor)
VALUES ('Writing Compilers and Interpreters', 'Ronald Mak')

The Component

Open Visual Basic and create a new ActiveX DLL. In the Project Properties dialog, set Project Name to SMTransferVB. Next check the Unattended Execution checkbox. This prevents the component from popping up message boxes or modal dialog boxes, which would then have to be dismissed before the component could continue responding to requests. Once you check this checkbox, another checkbox labeled Retained In Memory will appear. This setting allows your component to remain in memory even after its last instance has been destroyed. In COM+, transactional components must use Just-In-Time Activation, which means that instances are frequently created and destroyed. Therefore you should check this checkbox to prevent your component from being repeatedly loaded in and out of memory. You can find more information about this setting on MSDN at http://msdn.microsoft.com/training/free/chapters/mvs2/Topics/mvs00085.htm?WROXEMPTOKEN=621086Z65JjQqk8cXs0tH03WEy.

Next, in the Properties window, change the class name to Transfer, and set MTSTransactionMode to 2 - Requires Transaction.

Open the File pull-down menu and select Save Project to save both of them.

Referencing ADO, Message Queuing, and Transactions

If you're developing in Windows NT 4, you should install the Microsoft Data Access Components (MDAC) 2.5, which can be found at http://www.microsoft.com/data/download_250rtm.htm?WROXEMPTOKEN=621086Z65JjQqk8cXs0tH03WEy. If you're developing in Windows 2000 (W2K), you already have this version of MDAC (the latest version is 2.6).

Open Project References.. In the dialog box, your choices will depend on whether you're developing in NT 4 or W2K. For NT 4, check the Microsoft ActiveX Data Objects 2.5 library, the Microsoft Message Queue Object Library, and the Microsoft Transaction Server Type Library:

image1

For W2K, check the COM+ Services Type Library, the Microsoft ActiveX Data Objects 2.5 Library, and the Microsoft Message Queue 2.0 Object Library:

image2

Code

Our component will maintain a pointer to the IObjectContext interface, which we'll use to commit and abort transactions. We'll also define a constant for the ADO connection string:

Option Explicit

Dim m_pIObjectContext As ObjectContext

Const k_strDSN As String = _
"provider=sqloledb;server=watson;database=books;" & _
"uid=sa;password="

The next step is to implement the IObjectControl interface. We want our component to let COM+ know that it supports this interface:

Implements ObjectControl

The three methods of IObjectControl are:

Activate() and Deactivate() can be thought of as constructors and destructors for COM+ components. They should be used in place of Visual Basic's Class_Initialize() and Class_Terminate() functions.

Here, we'll use the Activate() function to obtain a pointer to the IObjectContext interface:

Private Sub ObjectControl_Activate()
    Set m_pIObjectContext = GetObjectContext()
End Sub

We also need to implement the Deactivate() function, though we won't put any code in it:

Private Sub ObjectControl_Deactivate()
End Sub

By the way, if you declare that your component implements a COM+ interface, but you don't actually implement all of the methods belonging to that interface, you'll get an error when trying to compile your component:

image3

The final IObjectControl method, CanBePooled(), tells COM+ whether each instance of the component should be placed in the instance pool after it is deactivated. Visual Basic doesn't support instance pooling, so our implementation of this function will return "False":

Private Function ObjectControl_CanBePooled() As Boolean
    ObjectControl_CanBePooled = False
End Function

Since we'll be passing strings to SQL stored procedures, we'll add a helper function to make sure those strings don't cause syntax errors:

Private Function PrepareSQLParameter (ByVal strParam As String) As String
strQuerystrParam = Replace(strParam, "'", "''")
    strParam = "'" & strParam & "'"
    PrepareSQLParameter = strParam
End Function

The MoveFromDatabaseToQueue() Method

The MoveFromDatabaseToQueue() method removes the specified title from the database and places it in the specified message queue. This method is transactional, so if there's a problem placing the title in the message queue, it's restored in the database.

Part of the work done by this method will actually take place inside a stored procedure named spSelectBookByTitleAndRemove:

CREATE PROCEDURE spSelectBookByTitleAndRemove
@strTitle varchar(50)
AS

SET NOCOUNT ON

I habitually use SET NOCOUNT ON to prevent the SP from returning the "X rows affected" information for any statements that otherwise don't return any results (e.g., INSERT, UPDATE, and DELETE).

DECLARE @Id AS int
DECLARE @strAuthor AS varchar(50)

SELECT @Id = Id, @strAuthor = strAuthor FROM tblBooks
WHERE strTitle = @strTitle

Our first query tries to find the book specified by the parameter strTitle. If we're not able to find such a book, we raise an error and exit the SP:

IF(0 = ISNULL(@Id, 0)) BEGIN
   RAISERROR('No such book', 11, 1)
   RETURN
END

When calling RAISERROR, be sure that the severity (the second parameter) is from 11 through 18. Otherwise, the error will not be propagated back to the caller. For more information see the KB article at http://support.microsoft.com/support/kb/articles/Q194/7/92.ASP?WROXEMPTOKEN=621086Z65JjQqk8cXs0tH03WEy.

If we find the book, we delete it from the database. Note that this deletion is not permanent, because we're working in a transaction that we can still decide to abort. Finally, we return the database ID, title, and author of the book:

DELETE FROM tblBooks
WHERE Id = @Id

SELECT @Id AS Id, @strTitle AS strTitle, @strAuthor AS strAuthor

Now we'll look at the MoveFromDatabaseToQueue() method. The first thing we'll do in this method is dimension our variables:

Public Sub MoveFromDatabaseToQueue(ByVal strTitle As Variant, _
ByVal strQueueName As Variant)

    Dim objRS As ADODB.Recordset
    Dim objStream As ADODB.Stream
    Dim strSQL As String

    Dim objMQInfo As MSMQ.MSMQQueueInfo
    Dim objMQQueue As MSMQ.MSMQQueue
    Dim objMQMessage As MSMQ.MSMQMessage

We'll use an ADO Stream object to extract XML from a Recordset. A Stream object provides a way to read and write a stream of bytes, which may be text or binary. For more information about Stream objects, see http://msdn.microsoft.com/library/psdk/dasdk/mdao1ajx.htm?WROXEMPTOKEN=621086Z65JjQqk8cXs0tH03WEy and http://msdn.microsoft.com/library/psdk/dasdk/pg_a4t83.htm?WROXEMPTOKEN=621086Z65JjQqk8cXs0tH03WEy.

The MSMQQueueInfo object is used to find and open a particular message queue, which will then be represented by the MSMQQueue object. We'll store the XML we extracted from the Recordset in the MSMQMessage object, which we'll then post to the queue.

Next, we'll prepare our SQL statement:

    strSQL = "EXEC spSelectBookByTitleAndRemove " & _
PrepareSQLParameter(strTitle)

Then we'll activate error handling, create our ADO Recordset and Stream objects, and execute the SQL statement. The ErrorHandler block will be shown later.

    On Error GoTo ErrorHandler
        Set objRS = CreateObject("ADODB.Recordset")
        Set objStream = CreateObject("ADODB.Stream")

        Call objRS.Open(strSQL, k_strDSN, adOpenForwardOnly, _
adLockReadOnly, adCmdText)

If there were any ADO errors, or if the specified title didn't match any records in the database, execution should immediately jump to the ErrorHandler block. Otherwise, we should have an open Recordset containing information about the specified title. We ask the Recordset to store its information in our ADO Stream, in XML format, and then we close the Recordset:

        Call objRS.Save(objStream, adPersistXML)
        Call objRS.Close

Now we create our MSMQQueueInfo object, and set its PathName property so that it will find and open the desired queue. MSMQ pathnames take the form "ComputerName\QueueName." My computer's name is Sherlock and I'm using a queue named BookQueue, so I would pass "Sherlock\BookQueue " as the value of the strQueueName parameter.

        Set objMQInfo = CreateObject("MSMQ.MSMQQueueInfo")
        objMQInfo.PathName = strQueueName

Next we open the queue. The first parameter to IMSMQQueueInfo::Open() specifies whether we want to send messages (MSMQ.MQ_SEND_ACCESS), receive messages (MSMQ.MQ_RECEIVE_ACCESS), or peek at messages (MSMQ.MQ_PEEK_ACCESS). Peeking at a message means viewing its contents without actually removing it from its queue, as we'll see later.

The second parameter specifies whether we want to allow other applications to access the queue while we have it open. To allow sharing, we pass MSMQ.MQ_DENY_NONE, and to deny it, we pass MSMQ.MQ_DENY_RECEIVE_SHARE. MSDN states that we must use MQ_DENY_NONE when opening a queue to send messages.

        Set objMQQueue = objMQInfo.Open(MSMQ.MQ_SEND_ACCESS, _
        MSMQ.MQ_DENY_NONE)

Next we create our MSMQMessage object. In the label (header), we store the title of the book being transferred. In the body, we store the XML which we extracted from our ADO Recordset into our ADO stream. To extract the string from the Stream, we call the ReadText() method.

        Set objMQMessage = CreateObject("MSMQ.MSMQMessage")
        objMQMessage.Label = strTitle
        objMQMessage.Body = objStream.ReadText

Finally, we call the Send() method of the MSMQMessage object. The first parameter is an MSMQQueue object, which determines the queue where the message will be sent. The second parameter determines whether the send operation is transactional. In our case it is, so we pass MSMQ.MQ_MTS_TRANSACTION. If the send operation weren't transactional, we would pass MSMQ.MQ_NO_TRANSACTION.

Unless the send operation raises an error, we close the message queue and call IObjectContext::SetComplete() to commit the transaction. Finally, we exit the function.

        Call objMQMessage.Send(objMQQueue, _
        MSMQ.MQ_MTS_TRANSACTION)

        Call objMQQueue.Close

        Call m_pIObjectContext.SetComplete
        Exit Sub

Now for the ErrorHandler block. If an error occurs at any point in this function, we'll abort the transaction by calling IObjectContext::SetAbort(). If a record was removed from the database, it will be restored. Also, if a message was sent to a queue, it will be recalled.

Next, we check to see whether we have an ADO Error, in which case we pass its information to Err.Raise(). If we don't have an ADO Error, the Err object will be pre-populated with the relevant error information:

ErrorHandler:
    Call m_pIObjectContext.SetAbort

    If objRS.ActiveConnection.Errors.Count > 0 Then
        With objRS.ActiveConnection.Errors(0)
            Call Err.Raise(.Number, .Source, .Description)
        End With
    Else
        With Err
            Call .Raise(.Number, .Source, .Description)
        End With
    End If

End Sub

Note that calling IObjectContext::SetAbort() in the event of an error is very important. In COM+, all methods of a transactional component default to being "happy." If we exit the function before calling IObjectContext::SetAbort(), even if we exit the function by raising an error, the transaction will still be committed. Therefore, we must call SetAbort() before raising any error.

The MoveFromQueueToDatabase () Method

The MoveFromQueueToDatabase() method removes the specified title from the specified message queue and places it in the database. This method is transactional, so if there's a problem placing the title in the database, it's restored in the message queue.

The first thing we'll do in this method is dimension our variables:

Public Sub MoveFromQueueToDatabase(ByVal strTitle As Variant, ByVal strQueueName As Variant)

    Dim objMQInfo As MSMQ.MSMQQueueInfo
    Dim objMQQueue As MSMQ.MSMQQueue
    Dim objMQMessage As MSMQ.MSMQMessage

    Dim objRSRead As ADODB.Recordset
    Dim objRSInsert As ADODB.Recordset
    Dim objField As ADODB.Field
    Dim objStream As ADODB.Stream

Once again, the MSMQQueueInfo object is used to find and open a particular message queue, which will then be represented by the MSMQQueue object. We'll use the MSMQMessage object to iterate through the message queue in search of the message containing the specified title.

The ADO Recordset objRSRead is populated from the XML stored in objMQMessage. This XML represents the Recordset, which we serialized earlier in the MoveFromDatabaseToQueue() method. To allow the Recordset to read the XML, we first place the XML in the ADO Stream, objStream. The other ADO Recordset, objRSInsert, is used to insert the book information into the database.

Now we'll activate error handling and create the MSMQQueueInfo object. Again, we set the PathName property to the strQueueName parameter:

    On Error GoTo ErrorHandler
        Set objMQInfo = CreateObject("MSMQ.MSMQQueueInfo")
        objMQInfo.PathName = strQueueName

To open the specified queue, we call the Open() method of objMQInfo. This time we pass MSMQ.MQ_RECEIVE_ACCESS as the first parameter. We're actually going to peek at the messages in the queue first, in order to find the message we're looking for, and then we'll receive it. Opening the queue with receive access allows us to both peek at and receive messages.

Because we'll be receiving messages from the queue, we pass MSMQ.MQ_DENY_RECEIVE_SHARE as the second parameter to prevent other processes from doing so at the same time.

        Set objMQQueue = objMQInfo.Open(MSMQ.MQ_RECEIVE_ACCESS, _
MSMQ.MQ_DENY_RECEIVE_SHARE)

We'll peek at the first message in the queue by calling the PeekCurrent() method of objMQQueue. The first parameter specifies whether we want to know the original destination queue for this message, which we don't. The second parameter specifies whether we want to see the body of the message. We don't need to ask for that until we actually receive the message. The third parameter specifies the timeout:

        Set objMQMessage = objMQQueue.PeekCurrent(False, False, 30)

Now we set up a loop to iterate through all messages in the queue. For each message, we check its label to see if it contains the book title we're looking for. If so, we exit the loop. If not, we peek at the next message by calling objMQQueue.PeekNext(). This method takes the same parameters as PeekCurrent(). Note that if you want to return to the beginning of the queue, you can call IMSMQQueue::Reset().

        Do Until objMQMessage Is Nothing
            If 0 = StrComp(objMQMessage.Label, strTitle, vbTextCompare) Then
                Exit Do
            End If

            Set objMQMessage = objMQQueue.PeekNext(False, False, 30)
        Loop

After iterating through the queue, if we didn't find the message we want, we report an error.

        If objMQMessage Is Nothing Then
            Err.Raise "No such book"
        End If

If we did find the message we want, we call IMSMQQueue::ReceiveCurrent(). The first parameter of this method is the same as the second parameter to IMSMQMessage::Send() - it determines whether the operation is transactional. In our case it is, so we pass MSMQ.MQ_MTS_TRANSACTION. This allows us to roll back the action of taking the message off the queue. If the receive operation weren't transactional, we would pass MSMQ.MQ_NO_TRANSACTION.

The remaining parameters are the same as those for IMSMQQueue::PeekCurrent() and IMSMQQueue::PeekNext(). Note that we pass True as the third parameter, specifying that we want to see the body of the message.

Afterward, we close the message queue:

        Set objMQMessage = _
objMQQueue.ReceiveCurrent(MSMQ.MQ_MTS_TRANSACTION, False, True, 30)
        Call objMQQueue.Close

Next we create our ADO Recordset and Stream objects:

        Set objRSRead = CreateObject("ADODB.Recordset")
        Set objRSInsert = CreateObject("ADODB.Recordset")
        Set objStream = CreateObject("ADODB.Stream")

So that the ADO Recordset objRSRead can read in the XML stored in the body of our message, we first place it in the ADO Stream:

        Call objStream.Open
        Call objStream.WriteText(objMQMessage.Body)
        Call objStream.SetEOS
        objStream.Position = 0

Now we can open objRSRead:

        Call objRSRead.Open(objStream)

Next we open objRSInsert, which we'll use to store the information about our book in the database. We pass in the name of the table to open, tblBooks, followed by our connection string. Passing adOpenKeyset gives us a read/write Recordset, and adLockOptimistic means that records are locked only while we're actually committing our updates to the database (as opposed to them being locked as soon as we begin editing them). Finally, passing adCmdTable specifies that we're opening a table, as opposed to executing a stored procedure.

        Call objRSInsert.Open("tblBooks", k_strDSN, adOpenKeyset, adLockOptimistic, adCmdTable)

Once the Recordset is open, we call its AddNew() method to begin inserting data. Then we simply iterate through all the fields in the objRSRead Recordset, copying them to objRSInsert. Note that we avoid the "Id " field, since this is an identity field.

        Call objRSInsert.AddNew
        For Each objField In objRSRead.Fields
            If objField.Name <> "Id" Then
                objRSInsert(objField.Name) = objField.Value
            End If
        Next

Once we've populated objRSInsert, we call its Update() method to insert our data:

        Call objRSInsert.Update

Close both Recordsets and the Stream:

        Call objRSRead.Close
        Call objRSInsert.Close
        Call objStream.Close

Finally, we call IObjectContext::SetComplete() and exit the method.

        Call m_pIObjectContext.SetComplete
        Exit Sub

If an error occurs at any point in this function, we'll abort the transaction by calling IObjectContext::SetAbort(). If a message was removed from a message queue, it will be restored. Also, if a record was inserted in the database, it will be deleted.

Next, we check both Recordsets to see whether we have an ADO Error, in which case we pass its information to Err.Raise(). If we don't have an ADO Error, the Err object will be pre-populated with the relevant error information. The first parameter of Err.Raise() is not optional, so we just pass in Err.Number.

ErrorHandler:
    Call m_pIObjectContext.SetAbort

    If objRSRead.ActiveConnection.Errors.Count > 0 Then
        With objRSRead.ActiveConnection.Errors(0)
            Call Err.Raise(.Number, .Source, .Description)
        End With
    ElseIf objRSInsert.ActiveConnection.Errors.Count > 0 Then
        With objRSInsert.ActiveConnection.Errors(0)
            Call Err.Raise(.Number, .Source, .Description)
        End With
    Else
        With Err
            Call .Raise(.Number, .Source, .Description)
        End With
    End If

End Sub

Installing the Component

Before installing the component, select the Component tab in SMTransferVB Properties.. and set Version Compatibility to Binary Compatibility. Make sure that the text box beneath the Binary Compatibility radio button reads SMTransferVB.dll. We're setting Binary Compatibility here because we won't be making any further changes to the component's interface. When a component is under development, you should set Version Compatibility to Project Compatibility. This setting ensures that your component keeps the same CLSID for each build, even if you make changes to any of its interfaces. When you're ready to deploy a component, set Version Compatibility to Binary Compatibility. This setting generates a new CLSID for your component if you change any of its interfaces, to ensure that the old component remains registered to provide backward compatibility for existing clients.

Finally, once the dll is created, install it in COM+ services.

The Client

Our client will allow us to move books between the database and different message queues, using transactions. Here's what it looks like in action:

image4

I've included code in the download for the client side UI, but won't explain it in detail like above. However, the main points of our Standard EXE form are:

After making the transfer, we repopulate the list of books in the source queue and the list of books in the database.

Creating the Message Queue

Open Computer Management from Start | Settings | Control Panel | Administrative Tools.

image5

Open Services and Applications | Message Queuing, right-click on Public Queues, and select New |Public Queue:

image6

In the Queue Name dialog, enter the BookQueue name, and check the Transactional checkbox.

image7

It's very important to check the Transactional checkbox - if you don't, you won't be able to send messages to this queue from within a transaction. Likewise, you can't send messages to a transactional queue when you're not inside a transaction. Again, for more information, see http://msdn.microsoft.com/library/psdk/msmq/msmq_guide_98vi.htm.

Running the Client

Here's what the client looks like on one of my computers, Sherlock. I have two message queues available, one on Sherlock and one on Watson, and both named BookQueue.

image8

Click (>>), and one of the books will be sent to the message queue:

image9

Returning to the Computer Management window, we can see the message in the queue named BookQueue:

image10

Double-click on the message and select the Body tab, and you can see that the message body contains XML data, which represents an ADO Recordset:

image11

Conclusion

In this article, we've looked at how to create a Visual Basic COM+ component which uses transactions to transfer data back and forth between databases and message queues. On the way, we've looked at programming against MSMQ, storing ADO Recordsets in messages, and the differences between interacting with transactional and non-transactional message queues, and local and remote message queues.

You may also want to look into queued components, a technology introduced by COM+. A queued component can receive method calls via a message queue, with no extra coding required. However, you still must write code to post messages and to receive messages other than method calls to your component. For more information, see Stephen Kaufman's article at http://www.asptoday.com/content/articles/20000119.asp?WROXEMPTOKEN=621086Z65JjQqk8cXs0tH03WEy.

Appendix A: COM+ Transaction States

In COM+, there are two bits that describe the state of a transactional component instance: the happy bit and the die-on-return bit.

There are four ways to "force the issue," by which I mean forcing all instances involved in a transaction to make an immediate decision about whether the transaction should be committed or rolled back:

It's recommended that every method of a transactional component call SetComplete() or SetAbort(). This increases scalability by deactivating instances after every method call. When an instance calls EnableCommit() or DisableCommit(), it enters what's known as conversational mode; instead of being deactivated after the current method returns, the instance hangs around waiting for another method call from the client. Conversational mode is discouraged because you can't count on a client to call more than one method of your component. If you want to perform a transaction, it's best to do so within a single method (though that method may include calls to methods of other transactional components).

Surprisingly, COM+ components are instantiated in conversational mode by default. The happy bit defaults to 1, while the die-on-return bit defaults to 0. You can fix this by calling SetComplete() and SetAbort() in all component methods, or by editing the component in Component Services. Right-click on each method of the component and select Properties. In the default tab of the Properties dialog, you'll see a checkbox labeled Automatically deactivate this object when this method returns. This causes both the happy and die-on-return bits to default to 1, and is equivalent to calling SetComplete() in that method. Of course, if something goes wrong inside the method, you should still explicitly call SetAbort().

References

Pinnock, Jonathan. (1998) Professional DCOM Application Development. Wrox Press Ltd.
Platt, David. (1999) Understanding COM+. Microsoft Press.
Sessions, Roger. (2000) COM+ and the Battle for the Middle Tier. John Wiley & Sons.

 
 
   
  RATE THIS ARTICLE
  Please rate this article (1-5). Was this article...
 
 
Useful? No Yes, Very
 
Innovative? No Yes, Very
 
Informative? No Yes, Very
 
Brief Reader Comments?
Your Name:
(Optional)
 
  USEFUL LINKS
  Related Tasks:
 
 
   
  Related ASPToday Articles
   
  • Queued Components in COM+ (January 19, 2000)
  •  
           
     
     
     
           
      Search the ASPToday Living Book   ASPToday Living Book
     
      Index Full Text Advanced 
     
     
           
      Index Entries in this Article
     
  • aborting transactions
  •  
  • Activate method
  •  
  • CanBePooled method
  •  
  • client side
  •  
  • COM+
  •  
  • COM+ components
  •  
  • COM+ transactions combined with message queuing
  •  
  • combined with COM+ transactions
  •  
  • committing and aborting transactions
  •  
  • committing transactions
  •  
  • components
  •  
  • creating message queues
  •  
  • Deactivate method
  •  
  • DisableCommit method
  •  
  • EnableCommit method
  •  
  • error handler
  •  
  • extracting from recordsets
  •  
  • extracting XML from recordsets
  •  
  • finding and opening message queues
  •  
  • finding messages
  •  
  • forcing immediate decision about whether to commit or roll back
  •  
  • IMSMQQueue object
  •  
  • inserting data
  •  
  • inserting data into databases
  •  
  • installing
  •  
  • IObjectContext interface
  •  
  • iterating through message queues
  •  
  • JIT Activation
  •  
  • keeping in memory after last instance is destroyed
  •  
  • message queuing
  •  
  • MoveFromDatabaseToQueue method
  •  
  • MoveFromQueueToDatabase Method
  •  
  • MSMQ
  •  
  • MSMQMessage class
  •  
  • MSMQMessage object
  •  
  • MSMQQueue class
  •  
  • MSMQQueueInfo class
  •  
  • objMQInfo object
  •  
  • objMQQueue object
  •  
  • objRSInsert object
  •  
  • objRSRead object
  •  
  • Open method
  •  
  • opening queues
  •  
  • path names
  •  
  • PeekCurrent method
  •  
  • PeekNext method
  •  
  • placing in ADO Stream
  •  
  • populating with XML
  •  
  • prevent the stored procedure from returning the \"X rows affected\" information
  •  
  • preventing message boxes and dialog boxes from popping up
  •  
  • RAISERROR
  •  
  • read/write Recordsets
  •  
  • ReceiveCurrent method
  •  
  • recordsets
  •  
  • representing message queues
  •  
  • Reset method
  •  
  • running client
  •  
  • Send method
  •  
  • setAbort method
  •  
  • SetComplete method
  •  
  • severity parameter
  •  
  • stored procedures
  •  
  • storing XML in MSMQMessage object
  •  
  • Stream object
  •  
  • transactional components
  •  
  • transactions
  •  
  • transactions combined with message queuing
  •  
  • Update method
  •  
  • XML
  •  
     
     
    HOME | TODAY | INDEX | SEARCH | REFERENCE | FEEDBACK | ADVERTISE | SUBSCRIBE
    .NET Framework Components Data Access DNA 2000 E-commerce Performance
    Security Admin Site Design Scripting XML/Data Transfer Other Technologies

     
    ASPToday is brought to you by Wrox Press (http://www.wrox.com/). Please see our terms and conditions and privacy policy.
    ASPToday is optimised for Microsoft Internet Explorer 5 browsers.
    Please report any website problems to [email protected]. Copyright © 2001 Wrox Press. All Rights Reserved.