Label Cloud

Thursday, November 16, 2006

WCF - Processing untyped messages from MSMQ binding

Now that .NET 3.0 is finally released, I am putting in some hours to see how it can be implemented in our infrastructure. Up front the power and flexibility of the framework is a bit overwhelming, however, the tools that are included are great. For example Service Configuration Editor has a great way to specify every setting required and optional for setting up WCF communication.

One of the components that I was looking to replace is a MSMQ Message Processing application. Basically it is a custom written MSMQ Trigger service. Well.. here goes.

WCF includes a binding for interconnecting with non-WCF MSMQ implementations. I've created the final prototype from the basic service sample, and created a ServiceContract interface and implementation class

[ServiceContract()]
public interface IMessageProcessor
{
   [OperationContract(IsOneWay = true)]
   void ProcessMessage(MsmqMessage<Stream> msg);
}

public class MessageProcessor : IMessageProcessor
{
   public void ProcessMessage(MsmqMessage<Stream> msg)
   {
      using (StreamReader sr = new StreamReader(msg.Body))
      {
         MessageBox.Show("Hello: " + sr.ReadToEnd());
         sr.Close();
      }
   }
}

Then added created a main form, and added start/stop events
 
internal static ServiceHost myServiceHost = null;
internal static void StartService()
{
   // Instantiate new ServiceHost
   myServiceHost = new ServiceHost(typeof(MessageProcessor));
   myServiceHost.Open();
}
internal static void StopService()
{
   // Call StopService from your shutdown logic (i.e. dispose method)
   if (myServiceHost.State != CommunicationState.Closed)
   myServiceHost.Close();
}
private void MainForm_Load(object sender, EventArgs e)
{
   StartService();
}
private void MainForm_FormClosed(object sender, FormClosedEventArgs e)
{
   StopService();
}

The next step is to setup the app.config configuration file.

<?xml version="1.0" encoding="utf-8"?>
<configuration>
   <system.serviceModel>
      <behaviors>
         <serviceBehaviors>
            <behavior name="Throttling">
               <serviceThrottling maxConcurrentCalls="2" />
            </behavior>
         </serviceBehaviors>
      </behaviors>
      <bindings>
         <msmqIntegrationBinding>
         <binding name="NewBinding0" exactlyOnce="false" serializationFormat="Stream" />
         </msmqIntegrationBinding>
      </bindings>
      <services>
         <service behaviorConfiguration="Throttling" name="WCFMQListener.MessageProcessor">
            <endpoint address="msmq.formatname:DIRECT=OS:.\private$\testqueue" binding="msmqIntegrationBinding" bindingConfiguration="NewBinding0" contract="WCFMQListener.IMessageProcessor" />
         </service>
      </services>
   </system.serviceModel>
</configuration>

The most time I've spent was to figure out how to read the non-xml formatted message. The key is to declare the method as

void ProcessMessage(MsmqMessage<Stream> msg);

and to adjust binding in the configuration file

serializationFormat="Stream"

The message can be read just as easily as a binary array.


Share/Save/Bookmark

1 comment:

Snakefoot said...

Thank you for posting this quick guide. It worked wonderful.

I had to modify my contract a bit, so the ProcessMessage became a catch all (Action = "*"):

[ServiceContract()]
public interface IMessageProcessor
{
[OperationContract(IsOneWay = true), Action = "*"]
void ProcessMessage(MsmqMessage < Stream > msg);
}