Message Queuing in C#.net

How to use Message Queuing in C#.net

Message Queuing in C#.net

Creating a Message Queue

Message queues can be created programmatically with the Create() method of the MessageQueue class.

With the Create() method, the path of the new queue must be passed. The path consists of the host name where the queue is located and the name of the queue. In the example, the queue MyNewPublicQueue is created on the local host. To create a private queue the path name must include Private$; for example, c:\kalit\Private$\MyNewPrivateQueue.

After the Create() method is invoked, properties of queue can be changed. For example, using the Label property, the label of the queue is set to Demo Queue. The sample program writes the path of the queue and the format name to the console. The format name is automatically created with a UUID that can be used to access the queue without the name of the server:

 
using System;
using System.Messaging;
 
namespace MessageQueuingInDot
{
   class Program
   {
      static void Main()
      {
         using (MessageQueue queue = MessageQueue.Create(@".\MyNewPublicQueue"))
         {
            queue.Label = "Demo Queue";
            Console.WriteLine("Queue created:");
            Console.WriteLine("Path: {0}", queue.Path);
            Console.WriteLine("FormatName: {0}", queue.FormatName);
         }
      }
   }
}

Finding a Queue

The path name and the format name can be used to identify queues. To find queues, you must differentiate between public and private queues. Public queues are published in the Active Directory. For these queues, it is not necessary to know the system where they are located. Private queues can be found only if the name of the system where the queue is located is known.

You can find public queues in the Active Directory domain by searching for the queue’s label, category, or format name. You can also get all queues on a machine. The class MessageQueue has static methods to search for queues: GetPublicQueuesByLabel(), GetPublicQueuesByCategory(), and GetPublicQueuesByMachine(). The method GetPublicQueues() returns an array of all public queues in the domain:

using System;
using System.Messaging;
 
namespace MessageQueuingInDot
{
   class Program
   {
      static void Main()
      {
         foreach (MessageQueue queue in MessageQueue.GetPublicQueues())
         {
            Console.WriteLine(queue.Path);
         }
      }
   }
}

The method GetPublicQueues() is overloaded. One version allows passing an instance of the MessageQueueCriteria class. With this class, you can search for queues created or modified before or after a certain time, and you can also look for a category, label, or machine name.

Private queues can be searched with the static method GetPrivateQueuesByMachine(). This method returns all private queues from a specific system.

Opening Known Queues

If the name of the queue is known, it is not necessary to search for it. Queues can be opened by using the path or format name. They both can be set in the constructor of the MessageQueue class.

Path Name

The path specifies the machine name and the queue name to open the queue. This code example opens the queue MyPublicQueue on the local host. To be sure that the queue exists, you use the static method MessageQueue.Exists():

using System;
using System.Messaging;
 
namespace MessageQueuingInDot
{
   class Program
   {
      static void Main()
      {
         if (MessageQueue.Exists(@".\MyPublicQueue"))
         {
            MessageQueue queue = new MessageQueue(@".\MyPublicQueue");
            //...
         }
         else
         {
            Console.WriteLine("Queue .\MyPublicQueue not existing");
         }
      }
   }
}

Depending on the queue type, different identifiers are required when queues are opened. The following table shows the syntax of the queue name for specific types.

Open table as spreadsheet Queue Type

Syntax

Public queue

MachineName\QueueName

Private queue

MachineName\Private$\QueueName

Journal queue

MachineName\QueueName\Journal$

Machine journal queue

MachineName\Journal$

Machine dead-letter queue

MachineName\DeadLetter$

Machine transactional dead-letter queue

MachineName\XactDeadLetter$

When you use the path name to open public queues, it is necessary to pass the machine name. If the machine name is not known, the format name can be used instead. The path name for private queues can only be used on the local system. The format name must be used to access private queues remotely.

Format Name

Instead of the path name, you can use the format name to open a queue. The format name is used for searching the queue in the Active Directory to get the host where the queue is located. In a disconnected environment where the queue cannot be reached at the time the message is sent, it is necessary to use the format name:

 
MessageQueue queue = new MessageQueue(
      @"FormatName:PUBLIC=");

The format name has some different uses. It can be used to open private queues and to specify a protocol that should be used:

·         To access a private queue, the string that has to be passed to the constructor is FormatName:PRIVATE=MachineGUID\QueueNumber. The queue number for private queues is generated when the queue is created. You can see the queue numbers in the <windows>\System32\msmq\storage\lqs directory.

·         With FormatName:DIRECT=Protocol:MachineAddress\QueueName you can specify the protocol that should be used to send the message. The HTTP protocol is supported with Message Queuing 3.0.

·         FormatName:DIRECT=OS:MachineName\QueueName is another way to specify a queue using the format name. This way you don’t have to specify the protocol but still can use the machine name with the format name.

Sending a Message

You can use the Send method of the MessageQueue class to send a message to the queue. The object passed as an argument of the Send() method is serialized to the associated queue. The Send() method is overloaded so that a label and a MessageQueueTransaction object can be passed. Transactional behavior of Message Queuing is discussed later.

The code example first checks if the queue exists. If it doesn’t exist, a queue is created. Then the queue is opened and the message Sample Message is sent to the queue using the Send() method.

The path name specifies “.” for the server name, which is the local system. Path names to private queues only work locally.

using System;
using System.Messaging;
 
namespace MessageQueuingInDot
{
   class Program
   {
      static void Main()
      {
         try
         {
            if (!MessageQueue.Exists(@".\Private$\MyPrivateQueue"))
            {
               MessageQueue.Create(@".\Private$\MyPrivateQueue");
            }
            MessageQueue queue = new MessageQueue(@".\Private$\MyPrivateQueue");
            queue.Send("Sample Message", "Label");
         }
         catch (MessageQueueException ex)
         {
            Console.WriteLine(ex.Message);
         }
      }
   }
}

Following shows the Computer Management admin tool where you can see the message that arrived in the queue.



By opening the message and selecting the Body tab (see below) of the dialog, you can see that the message was formatted using XML. How the message is formatted is the function of the formatter that’s associated with the message queue.



Message Formatter

The format in which messages are transferred to the queue depends on the formatter. The MessageQueue class has a Formatter property through which a formatter can be assigned. The default formatter, XmlMessageFormatter, will format the message in XML syntax as shown in the previous example.

A message formatter implements the interface IMessageFormatter. Three message formatters are available with the namespace System.Messaging:

·         The XmlMessageFormatter is the default formatter. It serializes objects using XML.

·         With the BinaryMessageFormatter, messages are serialized in a binary format. These messages are shorter than the messages formatted using XML.

·         The ActiveXMessageFormatter is a binary formatter, so that messages can be read or written with COM Objects. Using this formatter, it is possible to write a message to the queue with a .NET class and to read the message from the queue with a COM object or vice versa.



Sending Complex Messages

Instead of passing strings, it is possible to pass objects to the Send() method of the MessageQueue class. The type of the class must fulfill some specific requirements, but they depend on the formatter.

For the binary formatter the class must be serializable with the [Serializable] attribute. With the .NET runtime serialization all fields are serialized (this includes private fields). Custom serialization can be defined by implementing the interface ISerializable.

XML serialization takes place with the XML formatter. With XML serialization all public fields and properties are serialized. The XML serialization can be influenced by using attributes from the System.Xml.Serialization namespace.

Receiving Messages

To read messages, again the MessageQueue class can be used. With the Receive() method a single message is read and removed from the queue. If messages are sent with different priorities, the message with the highest priority is read. Reading messages with the same priority may mean that the first message sent is not the first message read because the order of messages across the network is not guaranteed. For a guaranteed order, transactional message queues can be used.

In the following example, a message is read from the private queue MyPrivateQueue. Previously a simple string was passed to the message. When you read a message using the XmlMessageFormatter, you have to pass the types of the objects that are read to the constructor of the formatter. In the example, the type System.String is passed to the argument array of the XmlMessageFormatter constructor. This constructor allows either a String array that contains the types as strings to be passed or a Type array.

The message is read with the Receive() method and then the message body is written to the console:

using System;
using System.Messaging;
 
namespace MessageQueuingInDot
{
   class Program
   {
      static void Main(string[] args)
      {
         MessageQueue queue = new MessageQueue(@".\Private$\MyPrivateQueue");
         queue.Formatter = new XmlMessageFormatter(
               new string[] {"System.String"});
 
         Message message = queue.Receive();
         Console.WriteLine(message.Body);
      }
   }
}

The Receive() message behaves synchronously and waits until a message is in the queue if there is none.

Enumerating Messages

Instead of reading message by message with the Receive() method, an enumerator can be used to walk through all messages. The MessageQueue class implements the interface IEnumerable and thus can be used with a foreach statement. Here, the messages are not removed from the queue, but you get just a peek at the messages to get their content:

 
MessageQueue queue = new MessageQueue(@".\Private$\MyPrivateQueue");
queue.Formatter = new XmlMessageFormatter(
      new string[] {"System.String"});
foreach (Message message in queue)
{
   Console.WriteLine(message.Body);
}

Instead of using the IEnumerable interface, the class MessageEnumerator can be used. MessageEnumerator implements the interface IEnumerator, but has some more features. With the IEnumerable interface, the messages are not removed from the queue. The method RemoveCurrent() of the MessageEnumerator removes the message from the current cursor position of the enumerator.

In the example, the MessageQueue method GetMessageEnumerator() is used to access the MessageEnumerator. With the MessageEnumerator the method MoveNext() takes a peek message by message. The MoveNext() method is overloaded to allow a timespan as an argument. This is one of the big advantages when using this enumerator. Here, the thread can wait until a message arrives in the queue, but only for the specified timespan. The Current property, which is defined by the IEnumerator interface, returns a reference to a message:

MessageQueue queue = new MessageQueue(@".\Private$\MyPrivateQueue");
queue.Formatter = new XmlMessageFormatter(
      new string[] {"System.String"});
 
using (MessageEnumerator messages = queue.GetMessageEnumerator())
{
   while (messages.MoveNext(TimeSpan.FromMinutes(30)))
   {
      Message message = messages.Current;
      Console.WriteLine(message.Body);
   }
}

Asynchronous Read

The Receive method of the MessageQueue class waits until a message from the queue can be read. To avoid blocking the thread, a timeout can be specified in an overloaded version of the Receive method. To read the message from the queue after the timeout, Receive() must be invoked again. Instead of polling for messages, the asynchronous method BeginReceive() can be called. Before starting the asynchronous read with BeginReceive(), the event ReceiveCompleted should be set. The ReceiveCompleted event requires a ReceiveCompletedEventHandler delegate that references the method that is invoked when a message arrives with the queue and can be read. In the example the method MessageArrived is passed to the ReceivedCompletedEventHandler delegate:

MessageQueue queue = new MessageQueue(@".\Private$\MyPrivateQueue");
queue.Formatter = new XmlMessageFormatter(
      new string[] {"System.String"});
 
queue.ReceiveCompleted +=
   new ReceiveComletedEventHandler(MessageArrived);
queue.BeginReceive();
// thread does not wait

The handler method MessageArrived requires two parameters. The first parameter is the origin of the event, the MessageQueue. The second parameter is of type ReceiveCompletedEventArgs that contains the message and the async result. In the example, the method EndReceive() from the queue is invoked to get the result of the asynchronous method, the message:

 
public static void MessageArrived(object source,
      ReceiveCompletedEventArgs e)
{
   MessageQueue queue = (MessageQueue)source;
   Message message = queue.EndReceive(e.AsyncResult);
   Console.WriteLine(message.Body);
}

 

By Kalit Sikka   Popularity  (3509 Views)