Working with Microsoft Message Queues in C#
 
Published: 13 Jul 2004
Unedited - Community Contributed
Abstract
This article highlights the use of Microsoft Message Queues on Windows 2003 / XP by using a Windows Form to add and delete message queues, messages (text and DataSets), as well as touching on some of the nuances of retrieving and casting message bodies. The code sample with this article can be used in both Windows Forms and ASP.NET applications as a starting point for building your own MSMQ components.
by Steven Barden
Feedback
Average Rating: This article has not yet been rated.
Views (Total / Last 10 Days): 23606/ 43

The Case For Message Queues

[Download Code]
How nice it is to live in an age of ever increasing network speeds. Faster networks and broadband Internet connections make work and play ever more enjoyable. But network problems still happen, and when your application cannot talk to all of it's components, still common enough in distributed applications, it can cause real problems. Most of the time we develop application exception code for this, but informing the user or components may not be enough. It may be vital to your application (and also, your job) that work still progresses. Enter Microsoft Message Queues.
 
We use message queuing every day. When you use your credit card, you may know that many companies do not process the actual transactions until the end of the business day. You may have heard people refer to 'batching out' at night, the act of using a modem to send all of their queued credit card transactions to the bank.  This is an example of message queuing, but probably the best example of message queuing is email. Someone sends an email, a server collects it, and it sits there until someone retrieves and reads it.
 
Also consider the use of message queues from a bank's perspective. Say that this example bank receives transaction information at various globally distributed locations. It's a safe bet that requirements dictate that remote data containing information about these transactions is not lost under any circumstances. But being a global system, it is also a safe bet that this whole system will have to contend with high network traffic, high central processing load, or link outages. Message queues are a good way to store this data in transit.

As a side note, just as there are server and client processing rules for email, message queues can be manipulated to follow business rules as well, although we will not cover that topic in this application.

About Message Queues And This Application

[Download Code]
Before I continue, allow me to acknowledge that a SQL Server database can act as a message queue. In fact, this concept is used quite often by many developers, but message queues have two immediate advantages over the use of SQL Server. The first advantage of message queues over SQL Server databases in temporarily storing data is flexibility. Message queues are easier to access when you consider the standard security and operational overheard involved in using SQL servers (security, access, drivers). The other major advantage in favor of message queues is cost--they are free.  Message queuing is built into the Windows business platforms that you have already purchased.

This application was developed on Windows Server 2003 acting as a member server in Active Directory, but you can use Windows XP as a stand-alone workstation. Message queues can be loaded during the installation of Windows or by adding the components via the Control Panel. The type of message queues that are available depend on your Active Directory connectivity. If you are in an Active Directory, you can make public message queues available that may be listed in the Active Directory.

If your development target system is not part of an Active Directory, your message queues will be private only. Once you have loaded the message queue components, you can right-click the server icon on your start menu or otherwise access the components via the Server Management controls. In this case I elected to use private message queues as they do not require Active Directory. The basic use of either type of queue is the same, but Active Directory-connected message queues offer functionality we will not cover here. I have also elected to pull data from the venerable old Pubs database via a local SQL Server database. You can find the connection string and change it at will to what you want, even using an Access DB as long as you configure your drivers accordingly.

With that described, let's get started by describing a little about this example application.

This example is broken out into a Windows Form and a functions class. This application is designed to work with text and DataSets in the local private queue. Other objects can be stored, but I have not coded the 'filters' for this. What this means is that storing your objects in a queue is simple, but retrieving them is a bit more work. We will cover this more later.

Allow me to explain why I used a Windows Form application versus an ASP.NET application. I am a web developer by trade, but my goal was to develop and distribute an easily-debugged application, so you could begin stepping thru the code quickly. This example does not require that you configure an ASP.NET environment, but the functions class can be dropped directly into an ASP.NET application. With the exception of a tree view method used by the Windows Form, all of this code can be used by ASP.NET. In fact, the tree view method should be easily modified for ASP.NET use as well.

Now let's consider the interface provided. Three group boxes are used to separate the text message, DataSet message, and tree view / queue manipulations sections. The text creation portion is fairly straight forward. Simply type in a label used as the name of the message and a text box to contain the data. The DataSet section is a little more involved but does the same thing with a bit more code added to allow you to make you own queries and clear the Windows Form DataGrid. A global DataSet is used to keep things simple. The last section is the message queue tree group. Here you can create and delete queues, refresh the tree view, and peek into messages.

This brings us back to the basic functionality of a message queue, which is to hold data. You will of course need to not only store but also retrieve data. Saving data is very simple, but retrieving data is a bit more involved. The first function to know about is the MessageQueue.Receive method. It does what it says, but it will remove the message from the queue. If you want to retrieve a copy of the Message, you will want to use the Peek method. Other than view and remove, the two methods are mostly the same.

I have elected to code for the Peek method only, as you will easily be able to adapt this code for your own needs upon seeing how the Peek method works. The other point to know about the Peek method (Receive as well) is that it will take the top message from the stack to process. For this reason I skipped the Peek method and moved right to the PeekByID method. Finally there is a delete queue function. You can delete the queue and all of it's messages, but you will be prompted to confirm that you are sure that you want to delete the queue if it contains messages.

A More Detailed Look At The Methods

[Download Code]
The WriteTextToQueue and WriteDSToQueue methods are functionally similar. In fact with a small amount of code modification they could be combined into a parameterized single method. You will notice that I make constant use of 'out' parameters in functions. It is my preference to make the output of most methods a Boolean indicating success or failure. The functionality of the tree view is fairly straight forward, but there is one point to note.

When the tree is populated, the nodes are listed as the Name (Label) of the Message, and the Tag field is used to denote whether the node is a Queue or a Message. What is not gathered and used in the tree view is the Id of the message. This is very important and after I reached the point where I wanted to use the PeekByID method I found that I had to find the Id of the message I wanted to work with. I considered this and decided that I would reiterate the messages in an individual queue and match the Label with the desired Message Id. You will find that the MessagePeek and RefreshTree methods both contain examples of iterating messages, differing only in the choice of objects used to do so. I suggest that you review each method to find which works best for you.

One point to take particular note of is the Path used to work with a queue. Notice that since I am working with private queues, the Path is in the form of ' .\private$\<queuename> '. Be careful not forget to include the leading '.\' in the string. Depending on how you pull the sting, you may or may not retrieve the leading '.\'. Also, because we are using C#, and you will want to list the characters in this sting as either '.\\private$\\<queuename>' or '@".\private$\<queuename>"'. If you run into problems where the error states that the queue name is not listed in the Directory Service (DS), check to make sure that the queue path is properly escaped and prefixed. You will notice that I use the line 'MessageQueue mq = new MessageQueue( ".\\" + Path ); ' in the MessagePeek method. Upon receiving errors at one point, I noticed that the prefix was missing and decided to correct it in this fashion because I did not want to take the time to track down why it was happening here when it was not happening in other places.  This is never a good practice in production development, but it provided a memorable example of this problem and one way to fix it.

Up until this point I have glossed over most of the functionality, knowing that you can easily find what you need by stepping thru the code, but I will now focus on some of the more interesting details.

As I described earlier, I had to iterate the messages to find the Message Id for the message I wanted to retrieve. You will find that code in the MessagePeek method. Once I found the Id, and used it locate the message I wanted, I had to cast the body of the message in the format that was required to use the data. When you create an object to contain the data in the body of the message, you must first create a message queue formatter. This is used to make sure that if you Send a DataSet, you receive a DataSet. When you create the message formatter, you need to add the object types that the formatter will require to properly assist you in using the contents of the message.

As another side note, while I do not go into detail in this application, the formatter can be either an XML or Binary formatter. This example application uses the XML version of the message queue formatter.

Wrapping It Up With The Real Working Code

[Download Code]
If any one line of code makes up the heart of this application, this is it.

mq.Formatter = new XmlMessageFormatter( 
new Type[] 
   { 
      typeof( System.String ) , 
      typeof( System.Data.DataSet ) 
   } ) ;

We use the XmlMessageFormatter object, and as part of it's parameters, we assign an array of Types. It is very important to note that you need to fully qualify the types because they cannot access the imports/using statements in your file. Create one for each object or data type that you expect to use in receiving from a queue. You can use your own types, which is very handy for storing and retrieving your own custom objects.

Next is the act of determining the type of the object that has been received, so you can properly cast the object.

1)

if ( message.Body.GetType().ToString() == "System.Data.DataSet"  )
{
     dataSet = (DataSet)message.Body ;
     Success = true ;
}

2)
if ( message.Body.GetType().ToString() == "System.String"  )
{
     rString = (string)message.Body ;
     Success = true ;
}

In section 1) you see that we test to determine if the type is a DataSet, and if so, we cast the message body into a DataSet object and we are done. Pass it on to its destination and continue processing. In section 2), the same is true for the string type. This can be used with custom objects and other tests can be done as well.

Finally, notice the TimeSpan component in the following code, also found in the MessagePeek method.

message = mq.PeekById( msgid , new TimeSpan( 0, 0, 1 ) ) ;

I will not go deep into the details, but if your object does not exist, you can have your caller wait a specified amount of time before it returns an error. Combine this with asynchronous calls, and you can now wait for an object to become available and process it.

I hope you have found this example application useful. The push today is to move away from batch processing, but this is not always practical, nor is always the best option.



User Comments

Title: Working with Microsoft Message Queues in C#   
Name: raj
Date: 2011-08-10 11:58:54 PM
Comment:
Web site is very helpful and efficient.
Title: Web site efficiency   
Name: Siphiwe Madi
Date: 2004-07-21 10:18:28 AM
Comment:
I think your Web site is very helpful and efficient.






Community Advice: ASP | SQL | XML | Regular Expressions | Windows


©Copyright 1998-2024 ASPAlliance.com  |  Page Processed at 2024-04-26 12:16:07 PM  AspAlliance Recent Articles RSS Feed
About ASPAlliance | Newsgroups | Advertise | Authors | Email Lists | Feedback | Link To Us | Privacy | Search