A basic MSMQ backed WCF server and client

After my recent post, MSMQ messages ending up in the "Transactional dead-letter messages" System Queue, I decided to build up my understanding of how MSMQ backed WCF services work by putting together a simple implementation to solidify my understanding of what I've read and to see it "in action" unencumbered by all the DI and other implementation specific code in the solution I'm working with day-to-day.

I've done this using console applications for the "server" (the process that reads items out of the message queue) and the "client" (the process that writes items to the message queue). Where I say "reads from"/"writes to" the message queue, strictly speaking the message queue is an implementation detail and what I'm doing is reading from/writing to a WCF channel that happens to be backed by a Message Queue. I've kept it simple by having the message transfer only be one way, effectively "fire and forget" on the part of the client.

Defining the service contract

The first thing to do (after creating an empty Visual Studio solution called SimpleMsmqWcf) is define the service contract that both the server and client will use to pass messages between them. For this purpose I created a new Class Library project called SimpleMsmqWcf.Common to house the interface that defines the contract. This library will eventually be referenced by both the server and client projects. Once I'd added a reference to the System.ServiceModel assembly, next up was defining the contract by creating an interface:

using System.ServiceModel;

namespace SimpleMsmqWcf
{
    [ServiceContract(Namespace = "http://My.Services/IServiceContract")]
    public interface IServiceContract
    {
        [OperationContract(IsOneWay = true)]
        void SendMessage(string message);
        [OperationContract(IsOneWay = true)]
        void SendNumber(long message);
    }
}

There are a couple of things to talk about with the interface, namely the attributes that have been applied to the class and its methods and the methods themselves. First up, the attributes!

ServiceContract - Just like any other self-respecting WCF service, this one needs to be annotated with the ServiceContract attribute so that WCF knows that this interface defines a WCF service. There are various additional properties that can be specified when annotating the interface with the ServiceContract attribute, I've dropped in Namespace - mainly because the code I used as a reference did so and there seemed little need to remove it.

OperationContract - Again, just like any other WCF service, the methods that form part of the service interface which must be implemented by the server and are callable by the client get annotated with this attribute. In addition to applying the attribute, I've also set the IsOneWay property to true. The first paragraph of the "Remarks" for this property explain why applying this attribute behaves differently to having a void returning method (my emphasis):

Use the IsOneWay property to indicate that an operation does not return a reply message. This type of operation is useful for notifications or event-style communication, especially in two-way communication. Without waiting for an underlying response message, callers of one-way operations have no direct way to detect a failure in processing the request message. (Service applications that use reliable channels and one-way operations can detect a message delivery failure at the channel level. For details, see Reliable Sessions Overview.)

This is the bit of magic that means the messages get treated as "fire and forget". A void returning method on a ServiceContract may still, in the absence of this property, push data along the underlying channel to indicate to the calling application (at the WCF level) whether the request was received/processed correctly or not.

I've created two methods on the interface, one that takes a string as its parameter and another that takes a long. This is for no reason other than to reinforce the fact that an MSMQ backed WCF service is no different, really, to any other WCF service. The fact that the requests to the service are all funneled through one queue has no bearing on the number of methods, or the signature of those methods. WCF bundles the data up into an XML blob that it then unbundles when the server reads the request.

Now the interface is defined, the next thing to do was define the client that writes messages to the service, and therefore into the message queue.

Creating the client

As with defining the service contract, the first thing to do is to create a new project. In this case the project is called (this is where my lack of imagination when it comes to naming things shows!) SimpleMsmqWcf.Client. As I mentioned earlier in the post, I created this project as a Console App, I could, of course, have created the project as a web application, or a desktop app using WinForms or WPF but that would've just meant more boilerplate, so on with the Console App..

First up is adding some references: As with the shared project, a reference to the System.ServiceModel assembly is required, along with a reference to the shared project. Once both of these have been added, we can get some code in. Here's the entirety of the code for Program.cs:

using System;
using System.ServiceModel;

namespace SimpleMsmqWcf.Client
{
    class Program
    {
        static void Main(string[] args)
        {
            var endpointAddress = "net.msmq://localhost/private/SimpleMsmqWcfQueue";
            
            var channelFactory = new ChannelFactory(
                new NetMsmqBinding(NetMsmqSecurityMode.None),
                new EndpointAddress(endpointAddress));

            var c = channelFactory.CreateChannel();

            c.SendMessage($"The current time is {DateTime.Now}");
            c.SendNumber(DateTime.Now.Ticks);
        }
    }
}

This is fundamentally the same as the code I used in my previous post. To decompose it, there are four parts present each separated by a blank line:

The endpoint address - This is the WCF address that should be used to send the messages to. The example here basically specified that I'm using a private queue called SimpleMsmqWcfQueue that's present on the local machine. Nothing very exciting.

The channel factory - In order to obtain a channel (a method of communicating with the service) I first have to obtain a factory to return a channel instance. This code achieves that by creating a channel factory that will create channels that use MSMQ and are looking at an endpoint with the address I previously specified.

A channel - So we can send messages, we need a channel instance to send them through. That's all this line of code does.

Send some messages - As there are two methods on the interface that defines the service contract, I've pushed a message through both, with the SendMessage call using string interpolation (referenced for anyone that's unfamiliar with this and wonders if it's a WCF thing, it's not!).

Before this program can be run, the message queue needs to be created, which can be achieved with the following steps:

  1. Make sure you've got the Message Queuing component installed on your machine
  2. Open Computer Management (Start > Run > "compmgmt.msc")
  3. Navigate down through the tree to Computer Management > Services and Applications > Message Queuing
  4. Right-click on Private Queues and choose New > Private Queue
  5. Name the queue SimpleMsmqWcfQueue and tick the Transactional checkbox

Keep Computer Management open, it's needed for the next part.

Aside: Looking at the messages in the message queue

Before we create the server, let's run the client application to drop some messages into the queue and then go and have a look at them. Hit F5, click Start on the toolbar, or use one of the Start options on the Debug menu, whatever your preference is. Assuming no exceptions are thrown, Computer Management can be used to have a look at the created messages. 

Expand the Private Queues node in the tree and look for the SimpleMsmqWcfQueue queue, if it's not there, right-click on Private Queues and choose Refresh. Now expand that queue and click into Queue Messages. The middle pane should look something like this:

Computer Management showing the queue with two messages present that were created by the SimpleMsmqWcf.Client console application

We can now open one of the messages by double clicking it, which brings up the Properties window, click across to the Body tab and look at the content of the message that was sent, which looks a little bit like this:

The body of a message sent from the Console application client, as seen in its Properties in the queue in Computer Management

Just at the bottom of the window is the text The current t which is the start of the text we sent with the call to c.SendMessage in the client console app. There's also two messages in the queue, so both of the messages we sent have been stored, all is good.

Creating the server

The last piece of the puzzle is to create the server, the console application that's going to sit and read messages from the message queue and react to them. So, I created a console application called SimpleMsmqWcf.Server and again added references to System.ServiceModel and the SimpleMsmqWcf.Common project. Once that's done, the first thing is to create a class that implements the IServiceContract interface. That looks a little something like this:

using System;
using System.ServiceModel;

namespace SimpleMsmqWcf.Server
{
    [ServiceBehavior(AddressFilterMode = AddressFilterMode.Any, InstanceContextMode = InstanceContextMode.PerCall, ConcurrencyMode = ConcurrencyMode.Multiple)]
    public sealed class ServiceContractHost : IServiceContract
    {
        public void SendMessage(string message)
        {
            Console.WriteLine($"{DateTime.Now} [{nameof(SendMessage)}]: {message}");
        }

        public void SendNumber(long message)
        {
            Console.WriteLine($"{DateTime.Now} [{nameof(SendNumber)}]: {message}");
        }
    }
}

As with the client, there's nothing exciting or remarkable about this code as it's simply a class that implements the interface and does stuff with the messages it receives. The ServiceBehaviour attribute has been applied, but that's only because I copied it from the solution that I've been working with. Determining what is and isn't needed, and why it's there is left as an exercise for the reader ;-)

Now we've got something that can process messages passed to the service, we need to spin it up. As this is a console app, I've put a simple host together that also has a "heartbeat" every second so that it can be seen in the console that it's still running:

using System;
using System.ServiceModel;
using System.Threading;

namespace SimpleMsmqWcf.Server
{
    class Program
    {
        public static ServiceHost _host;
        public static Thread _heartBeatThread;

        static void Main(string[] args)
        {
            _host = new ServiceHost(typeof(ServiceContractHost));
            _host.Open();
            _heartBeatThread = new Thread(new ThreadStart(WriteHeartBeat));
            _heartBeatThread.Start();
            _heartBeatThread.Join();
        }

        public static void WriteHeartBeat()
        {
            while (true)
            {
                Console.WriteLine("{0} Sleeping...", DateTime.Now);
                Thread.Sleep(1000);
            }
        }
    }
}

The only interesting lines here are thw first two in Main where an instance of the service host is created and its Open method is called (this is actually a method on CommunicationObject which is a couple of levels up the inheritance tree). Before we can actually run the code, some settings are needed in App.config so that WCF knows how to configure the ServiceHost instance, this is different to the client where I've applied the configuration in code:

<system.serviceModel>
  <bindings>
    <netMsmqBinding>
      <binding name="msmqBinding" receiveRetryCount="1" maxRetryCycles="2" retryCycleDelay="00:00:05" receiveErrorHandling="Move">
        <security mode="None" />
      </binding>
    </netMsmqBinding>
  </bindings>
  <behaviors>
    <serviceBehaviors>
      <behavior name="myBehaviour">
        <serviceMetadata httpGetEnabled="false" />
        <serviceThrottling maxConcurrentSessions="24" maxConcurrentInstances="12" maxConcurrentCalls="2147483647" />
        <dataContractSerializer maxItemsInObjectGraph="2147483646" />
        <serviceDebug includeExceptionDetailInFaults="true" />
      </behavior>
    </serviceBehaviors>
  </behaviors>
  <services>
    <service name="SimpleMsmqWcf.Server.ServiceContractHost" behaviorConfiguration="myBehaviour">
      <endpoint address="net.msmq://localhost/private/SimpleMsmqWcfQueue" binding="netMsmqBinding" bindingConfiguration="msmqBinding" contract="SimpleMsmqWcf.IServiceContract">
      </endpoint>
    </service>
  </services>
</system.serviceModel>

As with the attribute on the service host, most of this is boilerplate that I've lifted from the solution I'm using, but the key bits to call out are:

  • The service name: This matches the namespaced name of the class that's implementing IServiceContract
  • The end point address: This is the same address as I used in the client
  • The endpoint contract: This matches the namespaced name of the interface that's being implemented by the service host

Once this code is running, any messages that are added to the queue are processed, giving output that looks a little bit like this:

27/02/2018 20:03:39 Sleeping...
27/02/2018 20:03:40 Sleeping...
27/02/2018 20:03:41 Sleeping...
27/02/2018 20:03:42 Sleeping...
27/02/2018 20:03:43 Sleeping...
27/02/2018 20:03:44 Sleeping...
27/02/2018 20:03:45 Sleeping...
27/02/2018 20:03:46 Sleeping...
27/02/2018 20:03:47 Sleeping...
27/02/2018 20:03:48 Sleeping...
27/02/2018 20:03:48 [SendMessage]: The current time is 27/02/2018 20:03:48
27/02/2018 20:03:49 Sleeping...
27/02/2018 20:03:50 Sleeping...
27/02/2018 20:03:51 Sleeping...
27/02/2018 20:03:52 Sleeping...
27/02/2018 20:03:53 Sleeping...
27/02/2018 20:03:54 Sleeping...
27/02/2018 20:03:55 Sleeping...

That's job done (albeit with the output from SendNumber missing because I had breakpoints set in the client, and didn't realise until it came to writing this up that I'd missed a message!) and the code runs, sends messages, processes them and shows how it all hangs together. There's no exception handling present in this example, and with WCF there are plenty of ways exceptions can be thrown, so please, please, please don't use this code in production without adding appropriate handling and logging!

About Rob

I've been interested in computing since the day my Dad purchased his first business PC (an Amstrad PC 1640 for anyone interested) which introduced me to MS-DOS batch programming and BASIC.

My skillset has matured somewhat since then, which you'll probably see from the posts here. You can read a bit more about me on the about page of the site, or check out some of the other posts on my areas of interest.

No Comments

Add a Comment