Practical .NET

Picking Up Queue Messages: Strategy and Tactics

If you're using MSMQ to offload work from your Web site, you have a number of ways to pick up those messages, including processing those messages as soon as they turn up.

MSMQ provides a way of offloading work from your application to be processed at a later date or on another computer. In an earlier column, I showed how to write messages to a Microsoft Message Queue (MSMQ) for later processing. In this column, I'll show the code for reading your messages from the queue and, more important, give you some options on where to put that code.

Reading a Queue
The easiest way to pull a Message object from the queue is to use the MessageQueue object Receive method (you'll need to instantiate the MessageQueue object, passing it the name of the MSMQ queue you want to read to get access to your queue). Once you retrieve the message, you can pull your data from the Message Body property. If you've put an object in the Body property as I recommended in my previous column, you'll need to set the Message Formatter property to whatever formatter you used when your wrote the message. Even after setting the Formatter property, you'll need to cast the resulting object to the right type. This code does all of that:

Dim q As New MessageQueue(".\private$\emails")
Dim emm As EmailMessage
Dim msg As Message

msg = q.Receive()
Dim msgType(0) As Type
msgType(0) = GetType(EmailMessage)
msg.Formatter = New XmlMessageFormatter(msgType)

emm = CType(msg.Body, EmailMessage)

There is one wrinkle: If the Receive method doesn't find a message, the method waits until a message appears. So this code will run until all the messages are processed and then stop, waiting for another message. If you're writing a program that's supposed to process all the messages in a queue and then shut down (as part of your nightly processing, for example), this isn't going to work for you.

To prevent the Receive method from waiting forever for a message, you can pass a TimeSpan object specifying how long the method should wait for a message to turn up. Unfortunately, when the Receive method gives up waiting, it throws an exception. To prevent this exception from bubbling up and terminating your application, you'll need to wrap the method in a Try…Catch block. Of course, the Receive method could throw an exception for some other reason, so you should check to see if the problem really was a timeout error.

The following code causes the Receive method to wait two seconds before timing out:

Try
  msg = q.Receive(New TimeSpan(0, 0, 2))
Catch ex As Exception
  If ex.GetType Is GetType(MessageQueueException) Then
    Exit Sub
  Else
     Throw ex
  End If
End Try

If the Receive method does time out, the code quietly exits. This is because, presumably, all the messages have been processed. If the Receive method throws some other exception, that exception is passed onto the application.

Leveraging a Windows Service
If you want to process messages as they come in, one solution is to create a Windows Service. The Windows Service can process messages as they arrive, occasionally falling behind if messages arrive faster than the service can process them. Hopefully, though, once the rate of messages slows down, the service will (eventually) catch up. Effectively, even though you're using a queue, you get an "almost-real-time" response.

Creating a Windows Service is easy in Visual Studio: Just select File | New | Project to open the Add New Project dialog box. You'll find a Windows Service project template in the Windows section. Once Visual Studio has created your Windows Service project, you can rename the default class (Service1) to some more meaningful name.

You put your code to process a queue in the service class OnStart method. The simplest way to process messages as they come in is just to call the Receive method in an infinite loop. The Receive method will wait for a message to appear, process it, then go back to wait for the next message:

Dim q As New MessageQueue(".\private$\emails")
Dim msg As Message

Do Until bolStop
  msg = q.Receive()
  '...process message if found...
Loop

The bolStop variable would be declared outside of the service's two methods (OnStart and OnStop). In the OnStop method (which is called by Windows as part of shutting the service down), you just need to set bolStop to False.

The problem is, of course, breaking out of the Receive method to check that bolStop has been set. Here, again, you'll want to pass a TimeSpan object to the Receive method to give the loop a chance to check to see if the bolStop variable has been changed.

Rather than checking each message as soon as it arrives, you could check for messages on a regular basis. To implement this solution, drag a Timer control from the Toolbox onto your Windows Service design surface. Once there, you can put your code inside the Timer's Tick event and set the Timer's Interval property to the number of milliseconds to wait between checking for messages.

The code in Listing 1 will wake up, process all of the messages on the queue and, once there are no more messages, exit.

Listing 1: Using a Timer to Read MSMQ Messages
Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
  Dim q As New MessageQueue(".\private$\emails")
  Dim msg As Message
  Dim bolStop As Boolean

  Do Until bolStop
    Try
      msg = q.Receive(New TimeSpan(0, 0, 10))
    Catch ex As Exception
      If ex.GetType Is GetType(MessageQueueException) Then
        bolStop = True
      Else
        Throw ex
      End If
    End Try
    '...process message if present...
  Loop
End Sub

There's at least one more option to consider when processing MSMQ messages: Launching a program every time a message appears to process that message. I'll discuss that option in my next column.

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

  • Hands On: New VS Code Insiders Build Creates Web Page from Image in Seconds

    New Vision support with GitHub Copilot in the latest Visual Studio Code Insiders build takes a user-supplied mockup image and creates a web page from it in seconds, handling all the HTML and CSS.

  • Naive Bayes Regression Using C#

    Dr. James McCaffrey from Microsoft Research presents a complete end-to-end demonstration of the naive Bayes regression technique, where the goal is to predict a single numeric value. Compared to other machine learning regression techniques, naive Bayes regression is usually less accurate, but is simple, easy to implement and customize, works on both large and small datasets, is highly interpretable, and doesn't require tuning any hyperparameters.

  • VS Code Copilot Previews New GPT-4o AI Code Completion Model

    The 4o upgrade includes additional training on more than 275,000 high-quality public repositories in over 30 popular programming languages, said Microsoft-owned GitHub, which created the original "AI pair programmer" years ago.

  • Microsoft's Rust Embrace Continues with Azure SDK Beta

    "Rust's strong type system and ownership model help prevent common programming errors such as null pointer dereferencing and buffer overflows, leading to more secure and stable code."

  • Xcode IDE from Microsoft Archrival Apple Gets Copilot AI

    Just after expanding the reach of its Copilot AI coding assistant to the open-source Eclipse IDE, Microsoft showcased how it's going even further, providing details about a preview version for the Xcode IDE from archrival Apple.

Subscribe on YouTube

Upcoming Training Events