Advanced Basics

Using MSMQ with Visual Basic .NET

Ken Spencer

Code download available at:AdvancedBasics0211.exe(102 KB)

Contents

Moving On
Conclusion

Q I have an application that uses Windows® for the front end and has back-end components running on a server. How can I integrate the back-end components with applications running on another server? I've heard that Microsoft® Message Queuing (MSMQ) is a good choice for passing data between remote components. How does this work?

Q I have an application that uses Windows® for the front end and has back-end components running on a server. How can I integrate the back-end components with applications running on another server? I've heard that Microsoft® Message Queuing (MSMQ) is a good choice for passing data between remote components. How does this work?

A There are many ways to integrate applications with one another. Over the years, I have used everything from simple file shares to FTP to BizTalk® for integration. The advantage of messaging systems is that they are inexpensive to run and provide an asynchronous mechanism for communications.

A There are many ways to integrate applications with one another. Over the years, I have used everything from simple file shares to FTP to BizTalk® for integration. The advantage of messaging systems is that they are inexpensive to run and provide an asynchronous mechanism for communications.

In the February 2002 issue, I touched on MSMQ only briefly, so I'll delve into it further here. The asynchronous nature of MSMQ can make it really fast. Building applications that integrate with one another has always been possible, but often the resulting solution did not scale because of its synchronous nature.

Figure 1 shows a setup that can solve this problem. The back-end servers are clustered, hosting a set of components that can be called via Microsoft .NET Remoting or might just be component servers. Either way, you can use MSMQ to move the data from the back-end servers to the integration server. The queue can be hosted on any of the servers as long as they all have MSMQ installed.

Figure 1 Using MSMQ

Figure 1** Using MSMQ **

This scenario shows one advantage of the queued approach. The back-end servers simply put the data on the queue. That's it. The application does not wait for the other application and it doesn't need to do anything else related to the second application. It simply queues the data and goes on with other tasks. This allows you to build in this type of integration, while maintaining a minimal load on the existing application. The integration server is the one that will pick up the data and process it. The result is a decoupling of the input and output sides of the integration software. Thus the performance of one does not impact that of the other as long as there is data in the queue.

Now, let's look at how you might use MSMQ in this type of operation. Suppose you need to tie together two .NET-based applications, moving data from Application 1 to Application 2. You are going to deal with data about products (ID, Name, unit information) and you have a class (clsProduct) in Application 1 that represents a product. The code for clsProduct looks like this:

Public Class clsProduct Public ProductID As String Public ProductName As String Public UnitPrice As String Public UnitsInStock As String Public UnitsOnOrder As String End Class

This simple class can be loaded with data in the application, then passed around inside of that application, making it easy to work with product data. In the real world, this class would have many methods and properties, but for this discussion I have kept it very simple. My objective here is to take this class and pass it to MSMQ so I can use the same class in applications.

The first step is to mark the class as serializable so the .NET Framework can automatically serialize the class when the need arises. You set this attribute on a class like this:

<Serializable()> _ Public Class clsProduct

This class is implemented in the Products project, which was created as a Class Library. You can find this project and others mentioned in this column in the download (see the link at the top of this article).

The MSMQFSLoader project contains frmQer.vb, which loads the queue. This form implements a FileSystemWatcher control set to monitor the C:\XML folder. Whenever a products.xml file is dropped into this folder, the flsyswtchrXML_Changed event fires and executes the code that loads the queue. This code loads a DataSet with the XML from Products.xml and then calls the ProcessProduct procedure for each row in the DataSet.

The code for this event is shown in Figure 2. ProcessProduct is called each time through the For Each loop, passing in the data from the current row of the DataSet.

Figure 2 Loading the Queue

Dim ds As New DataSet() Dim dr As DataRow lblProcessMessage.Text = e.Name lblProductProcessing.Text = "Processing Products..." Application.DoEvents() Try ds.ReadXml(e.FullPath) For Each dr In ds.Tables(0).Rows ProcessProduct(CStr(dr("ProductID")), _ CStr(dr("ProductName")), _ CStr(dr("UnitPrice")), _ CStr(dr("UnitsInStock")), _ CStr(dr("UnitsOnOrder"))) Debug.WriteLine("ProductID = " _ & CStr(dr("ProductID"))) Next Catch exc As Exception lblStatus.Text = exc.Message End Try lblProductProcessing.Text = "Completed Product Processing"

Now let's turn to MSMQ. The .NET Framework provides the System.Messaging namespace that contains the classes I use with MSMQ. Combined with Visual Studio .NET, this makes working with queues a snap. To implement the queue in this form, I opened Server Explorer in Visual Studio .NET and opened the Message Queues folder under my workstation. Then I right-clicked Private Queues and selected Create Queue. At this point, you can simply follow the dialog prompts to create a new queue. The queue that I've used in this example is called products. Now your new queue should show up under Private Queues.

To add code to your application to use this queue, simply drag the new queue and drop it on the form. This will add an entry to the Components Tray of the designer and code will be added to the designer region to instantiate the queue and set its properties.

Take a look at how the queue is used in this app. The code for ProcessProduct is shown in Figure 3. The following line calls the Send method of the System.Messaging.MessageQueue class:

msmqProducts.Send(oProduct, "ID= " & ProductID)

The two arguments used in this example are the class (oProduct) and the label for this message ProductID. That's it. This simple statement drops the serialized version of the class on the queue.

Figure 3 ProcessProduct

Sub ProcessProduct(ByVal ProductID As String, _ ByVal ProductName As String, _ ByVal UnitPrice As String, _ ByVal UnitsInStock As String, _ ByVal UnitsOnOrder As String) Dim oProduct As New clsProduct() Try oProduct.ProductID = ProductID oProduct.ProductName = ProductName oProduct.UnitsInStock = UnitsInStock oProduct.UnitsOnOrder = UnitsOnOrder oProduct.UnitPrice = UnitPrice msmqProducts.Send(oProduct, "ID= " & ProductID) Catch exc As Exception lblStatus.Text = _ "Error occured in ProcessProduct — " & _ exc.Message Finally oProduct = Nothing End Try End Sub

Another nice thing about Visual Studio .NET is how Server Explorer lets you see the items in the queues. For instance, Figure 4 shows the Products queue after the load program has processed a file. You can clearly see the label for each ProductID. During development, this makes it easy to see when the items are dropped on the queue and if there is some type of problem.

Figure 4 Products Queue

Figure 4** Products Queue **

It's time to approach the other side of the integration problem: Application 2, which takes the data off the queue. This example is very straightforward. The ProcessProductsQueue procedure is called once by the timer and uses a single loop that continuously processes the queue by calling the Receive method (see Figure 5).

Figure 5 Receive Method Call

While True Dim msgText As String Dim eMsg As System.Messaging.Message Dim oProduct As clsProduct eMsg = msmqProductsIn.Receive eMsg.Formatter = New XmlMessageFormatter(New Type() _ {GetType(clsProduct)}) oProduct = CType(eMsg.Body, clsProduct) iTotal += 1 lblProductsProcessed.Text = CStr(iTotal) txtOutput.Text &= oProduct.ProductID & vbTab & _ oProduct.ProductName & vbTab & _ oProduct.UnitPrice & vbCrLf lblProductsProcessed.Text = iTotal.ToString Application.DoEvents() oProduct = Nothing End While

The formatter is assigned using this code:

eMsg.Formatter = New XmlMessageFormatter(New Type() _ {GetType(clsProduct)})

This lets the message know that it must use the XmlMessageFormatter for processing and that it will return a message of type clsProduct. The class is actually retrieved by this line of code:

oProduct = CType(eMsg.Body, clsProduct)

At this point, oProduct is an object that looks exactly like the object I put on the other end of the queue. This allows both applications to use the clsProduct class and work with it as needed for that particular application. This also makes handling the data cleaner since clsProduct knows how to handle the data and you do not need to worry about that in this application. Once oProduct is received from the queue, I could pull the data from it and save it to a database or process the data in any other manner. Of course, I could add functions to clsProduct that I could call and let it do the update for me. There is an endless set of possibilities for working with queues.

Moving On

There are a number of other things you should consider when using a message queue in your application. The big catch is that a message body is limited to 4MB. This can become an issue when you have a large amount of data to send. In these cases, you must break the data into smaller chunks and send it over MSMQ.

There are many other things to consider for production applications. It is not very likely that you will use an application with an infinite loop to process messages. Instead, you can use MSMQ Triggers, an optional feature of MSMQ that allows you to use a COM component to process messages. You can tell if MSMQ Triggers are installed by looking at the MSMQ snap-in (under Services and Applications in the Computer Management tool for Windows XP). You should see a folder for Message Queuing Triggers. If triggers are not installed, you can install them from Add / Remove Programs in the Windows XP and Windows .NET Server Control Panel; you can download the extensions for earlier versions).

You must create a new trigger rule for the trigger support, then attach that rule to a trigger. This is where you assign it to a component. Of course, if you create a component in .NET, then use RegAsm to register your component with COM.

As your apps increase in complexity, there are more things to keep in mind. When you design an MSMQ app, local reads have better performance than remote reads. MSMQ can be used for interprocess communications on a local computer. And it's best to avoid using queued data as a database store.

There are other issues related to asynchronous applications. For instance, when you place an item on a queue, how do you know that it traveled through the queue and was processed successfully? These two questions have been the bane of many asynchronous applications over time. The MessageQueueTransaction class was provided to solve this problem.

Using this class, you can create a transaction for entries to be placed on a queue. To use transactions, the queue itself must be transactional. You can indicate this with a checkbox setting (the default is not transactional) when you create the queue. Once you have set up the transactional queue, you can use the MessageQueueTransaction when you send and receive messages. This ensures that messages are pulled off the queue successfully.

You can also send and receive information using MSMQ to and from MQSeries using the MSMQ-MQSeries Bridge. This method provides a secure and flexible means to move data between .NET and mainframe applications. For more information on MQSeries, see the MSMQ-MQSeries Bridge Extensions reference.

Conclusion

There are many ways to integrate applications. Message queues are one flexible and cost-effective method. You can also use components like the FileSystemWatcher to assist with integration.

Other features in the .NET Framework, such as support for XML and Windows Services, make it easy to integrate applications. You can also combine these features with the new features of MSMQ to build flexible solutions. For instance, since DataSets are serializable, you can drop a DataSet on a queue and pull it off in another application—a very simple procedure. As you explore the possibilities, you may find other ways of moving data between apps that meet your specific needs more closely.

Send questions and comments for Ken to  basics@microsoft.com.

Ken Spencerworks for 32X Tech (https://www.32X.com) where he provides training, software development, and consulting services on Microsoft technologies.