Introduction
As part of a project I was working on, I was tasked with developing a web service based on Windows Communication Foundation (WCF) as a frontend to an MSMQ queue. It was an opportunity to learn a new technology, and I eagerly looked around for examples.
My initial attempts were quite frustrating. I found some samples on the web, but just could not get them to work. The terminology was quite confusing. My pace improved significantly when I found an excellent starter project on Dennis van der Stelt’s blog. I would enthusiastically recommend this to anyone who is just getting started with WCF. I finally found a sample demonstrating the use of WCF to submit messages to a queue, on MSDN’s Windows Communication Foundation to Message Queuing page.
My manager suggested simplifying the application to simply queue an XML message rather than an object (PurchaseOrder
), as shown in the article. This allows for the WCF service to act as an agnostic router, with no need to read or interpret the contents of the message placed on the queue. This project is the prototype that I created by piggy-backing on the MSDN example. I strongly recommend that a reader seeking to get started on WCF look at the original example in addition to the solution attached to this article.
Reviewing the code
Let’s start at the end, so we know where we are going right from the start.
The screen above shows a client application that has successfully submitted an XML document as a string to a WCF service. That is all that the client does, i.e., build an XML string and submit it to the service.
The output of the WCF service shows output from two components. The first two lines provide confirmation that our WCF service is up and running. The rest of the lines are output by an asynchronous processor that monitors the message queue. In this case, it simply outputs routing information from the MSMQ message label and the contents of the XML message. Note, the message can be any valid XML document; the processor does not care about its contents. The processor could be extended to deliver the message to any one of multiple endpoints based on the destination specified in the MSMQ message label. If this approximates the functionality you are seeking, then read on as we delve into the code that makes this solution possible.
The client
The client is a simple project that creates the XML message. I chose to read in the outline of the message from a file on disk. I then merge my sample data with the outline. The following code illustrates these actions:
string fileTemplate =
ConfigurationManager.AppSettings["ShipperTxnTemplate"];
fileDoc.Load(fileTemplate);
if (fileDoc.DocumentElement == null)
return;
string trnID = "00001001";
XmlNode rootNode = fileDoc.LastChild.LastChild.FirstChild;
rootNode.Attributes["txnid"].Value = trnID;
rootNode.Attributes["status"].Value = "Queued For Submission";
rootNode.Attributes["submitdate"].Value = DateTime.Now.ToString();
XmlNode fileNode = rootNode.FirstChild;
fileNode.Attributes["CompanyName"].Value = "FedEx Kinkos";
fileNode.Attributes["Phone"].Value = "425-884-9000";
Line 1 in the above snippet reads in the location of the template file from the client application configuration (app.config) file. Lines 6-14 merge the sample data with the outline. The message is now ready for delivery to the WCF service. The following code illustrates these actions:
string batchMsg = fileDoc.InnerXml;
MessageProcessorClient strClient =
new MessageProcessorClient("MessageResponseEndpoint");
MsmqMessage<String> strXmlMsg = new MsmqMessage<string>(batchMsg);
strXmlMsg.Label = "[WCFMQ_Client][WCFMQ_Service][ShipperTxn]";
using (TransactionScope scope =
new TransactionScope(TransactionScopeOption.Required))
{
strClient.SubmitStringMessage(strXmlMsg);
scope.Complete();
}
Line 1 in the snippet above converts the XMLDocument
object to a string. Line 2 instantiates a MessageProcessorClient
object from a definition within the client’s app.config file. The following snippet is a copy of the definition:
<endpoint name="MessageResponseEndpoint"
address="msmq.formatname:DIRECT=OS:.\private$\northwindqueue"
binding="msmqIntegrationBinding"
bindingConfiguration="MessageProcessorBinding"
contract="WCFMQService.IMessageProcessor">
</endpoint>
The definition comprises the ABCs of a WCF message. An article providing an overview to the endpoint definition can be found here. Line 3 instantiates a MsmqMessage
message containing the XML message. Line 4 sets the label for the message. It is this label that the WCF can use to identify the destination service and the type of the message. The message processor does not need to examine the contents of the message itself. Line 7 dispatches the MSMQ message to the WCF service.
The WCF service
I hope you will agree with me that creating the client was the easy part, creating the service is easier still. In fact, if you are using Visual Studio 2008, then there is no coding involved in creating the service or receiving messages from clients. Let's confirm this by looking at the Main()
method of the service.
static void Main(string[] args)
{
if (!MessageQueue.Exists(ConfigurationManager.AppSettings["queueName"]))
MessageQueue.Create(ConfigurationManager.AppSettings["queueName"], true);
MessageQueue Queue = new
MessageQueue(ConfigurationManager.AppSettings["queueName"]);
Queue.ReceiveCompleted += new ReceiveCompletedEventHandler(ProcessMessage);
Queue.BeginReceive();
Console.WriteLine("WCFMQService is running");
Console.WriteLine("Press <ENTER> to terminate service.");
Console.ReadLine();
}
Lines 1-2 check for the existence of an MSMQ queue, and if necessary create it. The name of the queue is defined in the service application configuration (app.config) file. Line 5 creates a handle to the queue and Line 7 attaches the message processing method (ProcessMessage
) to the queue. Finally, line 8 sets the service up to monitor the queue for messages. Every one of these lines has to do with the processing of messages on the queue. As I said earlier, there is no code involved in creating the service or receiving messages from clients.
Note: If you comment out line 8, then messages placed on the queue will be left there. Uncommenting it and running the service will result in processing and automatic removal of all messages on the queue. You can view the contents of the queue by starting the ‘Computer Management’ (compmgmt.msc) utility and checking the private queue ‘northwindqueue’ under the ‘Message Queuing’ sub-node of the ‘Services and Applications’ node.
If you don’t find the ‘Message Queuing’ sub-node, then you probably do not have MSMQ installed. You can install MSMQ using ‘Add/Remove Programs’. You can go to this MSDN page for instructions on installing MSMQ.
Finally, let’s look at the last portion of code for our solution – the message processor.
MessageQueue Queue = (MessageQueue)source;
Queue.Formatter = new XmlMessageFormatter(new Type[] { typeof(String) });
System.Messaging.Message msg = Queue.EndReceive(asyncResult.AsyncResult);
string[] tokenArr = msg.Label.Split(new char[] { '[', ']' });
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml((string)msg.Body);
Console.WriteLine(Environment.NewLine + "Processing incoming message "
+ Environment.NewLine + "Service Name:\t{0}"
+ Environment.NewLine + "Destination:\t{1}"
+ Environment.NewLine + "Document ID:\t{2}"
+ Environment.NewLine + "Message Contents:" +
Environment.NewLine + "{3}"
, tokenArr[1], tokenArr[3], tokenArr[5]
, FormatXML(xmlDoc.InnerXml, true));
Queue.BeginReceive();
Line 2 creates a reference handle to the queue. Line 3 creates a formatter that will enable us to read the contents of the message. Line 5 temporarily suspends the receiving of queue messages while the current message is being processed. Lines 7-18 read the message and output it to the console. This is the point in code where you can insert your custom code to interpret and act on the XML message received. Line 21 then resumes monitoring the queue for further messages. Note: The processing of queue messages is asynchronous, and this is the central advantage to using queues with a Windows Service. Placing all messages on the queue enables the service to return immediately to accept the next message without having to wait for the previous message to be completely processed. This can offer significant advantages for scalability.
Summary
The Windows Communication Foundation is a great framework for applications to interoperate with each other. You could have multiple applications exchanging messages via the queue. Each application can place messages on the queue, and read messages off the queue if it is addressed to them. They can do so by reading the label attached to each message. Alternately you can create a specialized WCF router that delivers messages to one or more endpoints. Either way, you have an elegant way to implement a service-oriented architecture for your applications.
References
History
- 03 Jan. 2009 - Initial submission.