Practical .NET

Asynchronous MSMQ Processing

Peter wraps up his look at offloading processing from the mainline of your application in order to improve response time by looking at processing message queues asynchronously.

In previous columns, I looked at setting up and writing to a Microsoft Message Queue and processing the messages in a Message Queue. In that last column, I used plain-old synchronous programming to read messages from the queue using the MessageQueue Receive method. That strategy has problems because the Receive method blocks execution until a message shows up on the queue. That's not necessarily a bad thing: In a Windows Service, you probably want your code to wait for a message and process the message as soon as it appears.

In many cases, however, that blocking action is not what you want. Sometimes you just want to check to see if a message is present, and if there is no message, go on to something else. Even in the examples I used in my previous columns, terminating processing was awkward because you had to set a time limit for the Receive method and catch the error that resulted when the Receive method "timed out." The code wasn't awful (and does work), but it's inelegant.

Using asynchronous processing with the Receive method involves about the same amount of code as synchronous processing, but is more appopriate when your application has something else to do than wait around for a message to show (for example, checking to see if you should terminate).

Setting Up the Queue
The first step in your asynchronous code is to set up your queue. The variable that holds your reference to the queue must be declared globally, because you'll be accessing the queue from multiple methods. If you're using Visual Basic, you'll need to declare that variable with the WithEvents keyword, like this:

Dim WithEvents q As MessageQueue

At some point early in the life of your application, you'll need to check to make sure the queue exists, create the queue if it doesn't and, finally, set your variable to a reference to the queue. That's what this code does:

If Not MessageQueue.Exists(".\private$\orders") Then
  MessageQueue.Create(".\private$\orders", False)
End If
q = New MessageQueue(".\private$\orders")

Now you're ready to begin processing your queue asynchronously. The easiest way is to call the queue's BeginReceive method:

q.BeginReceive()

Calling BeginReceive causes your application to wait for a message to appear on the queue -- but that wait is now processed on a separate thread. Your application will proceed to whatever line follows the call to BeginReceive.

Reading Messages
As messages show up on the queue, the queue fires a ReceiveCompleted event. If there are already messages on the queue, the event fires immediately after you call the BeginReceive method. To process a message asynchronously, you just need to tie some code to that event.

In Visual Basic, to tie an event handler method of your own to the event you can use the Handles keyword on the method's declaration. That method must accept the standard parameters for an event with the e parameter declared as ReceiveCompletedEventArgs:

Public Sub ReadQueue(sender As Object, e As ReceiveCompletedEventArgs) Handles q.ReceiveCompleted

In C# you tie an event handler to the event with code like this (you should put this code before the call to BeginReceive):

q.ReceiveCompleted += ReceiveCompletedEventHandler(ReadQueue);

Similarly, you can add an event handler dynamically in Visual Basic with the AddHandler keyword, and omit the Handles keyword on the method:

AddHandler q.ReceiveCompleted, AddressOf ReadQueueEvent

Within your method you use your queue variable's EndReceive method to process the message, passing the e parameter's AsyncResult property. This code retrieves the message that triggered the event, and assumes the body of the message contains an Order object formatted using the .NET XmlMessageFormatter class:

Dim msg As Message
Dim ord As Order

msg = q.EndReceive(e.AsyncResult)
Dim msgType(0) As Type	
msgType(0) = GetType(Order)
msg.Formatter = New XmlMessageFormatter(msgType)
ord = msg.Body

Calling EndReceive terminates the BeginReceive call. So, after you've done whatever processing you wanted to do with the message, you should set up to process the "next" message (which may, of course, have already arrived and be sitting on the queue). That's easy: Just call the BeginReceive method at the end of your event handler:

q.BeginReceive()

If all you want to do is some foreground processing while the BeginReceive method checks on your queue in the background, you can also call the EndReceive method outside of an event handler method. However, if there's no message on the queue, the EndReceive method will hang until a message appears on the queue.

This column finishes my discussion of using the MSMQ to offload processing from your application's mainline. It's an option you should explore whenever you're looking for a way to be more responsive to your users while at the same time handling long-running processes.

About the Author

Peter Vogel is a system architect and principal in PH&V Information Services. PH&V provides full-stack consulting from UX design through object modeling to database design. Peter tweets about his VSM columns with the hashtag #vogelarticles. His blog posts on user experience design can be found at http://blog.learningtree.com/tag/ui/.

comments powered by Disqus

Featured

  • Diving Deep into .NET MAUI

    Ever since someone figured out that fiddling bits results in source code, developers have sought one codebase for all types of apps on all platforms, with Microsoft's latest attempt to further that effort being .NET MAUI.

  • Copilot AI Boosts Abound in New VS Code v1.96

    Microsoft improved on its new "Copilot Edit" functionality in the latest release of Visual Studio Code, v1.96, its open-source based code editor that has become the most popular in the world according to many surveys.

  • AdaBoost Regression Using C#

    Dr. James McCaffrey from Microsoft Research presents a complete end-to-end demonstration of the AdaBoost.R2 algorithm for regression problems (where the goal is to predict a single numeric value). The implementation follows the original source research paper closely, so you can use it as a guide for customization for specific scenarios.

  • Versioning and Documenting ASP.NET Core Services

    Building an API with ASP.NET Core is only half the job. If your API is going to live more than one release cycle, you're going to need to version it. If you have other people building clients for it, you're going to need to document it.

  • TypeScript Tops New JetBrains 'Language Promise Index'

    In its latest annual developer ecosystem report, JetBrains introduced a new "Language Promise Index" topped by Microsoft's TypeScript programming language.

Subscribe on YouTube