Indigo: MSMQ Support, Queued Channels 

A few days ago, Indigo + Avalon RC Beta 1 went live on MSDN.

There are three packages to download, if you want to check out what's new in MSMQ integration scenarios and especially queued channels.

  1. Indigo Runtime RC Beta 1
  2. WinFX SDK RC Beta 1
  3. MSMQ 3.5 Beta for Indigo

While I don't want to get into a discussion on naming schemes (and yes, I do understand that genuine Beta 1 is reserved for Longhorn Beta 1, but this is too much), these three will give you an option to review what's been hidden on the MSMQ story.

Things to consider:

  • Try and install MSMQ 3.5 after native MSMQ support in Add/Remove Programs. It will make your life easier.
  • Indigo RC Beta 1 will only work on official .NET Framework Beta 2. It will not work on any previous CTPs.

Having that installed, we can write some MSMQ Indigo code. A simple calculator service has a simple service contract:

[ServiceContract]
public interface ICanCalculate
{
  [OperationContract(IsOneWay=true)]
  void CalcOp(Operation enmOp, int intA, int intB);
}

public enum Operation
{
  Add,
  Subtract,
  Multiply,
  Divide
}

All messages are processed asynchronously, therefore IsOneWay should be set to true on all operations. Any processing is not done at invocation time, but rather at service processing time, which decouples your client from your service. Service implementation, which implements ICanCalculate does the obvious:

public class CalculatorService : ICanCalculate
{
  [OperationBehavior(AutoEnlistTransaction=true, AutoCompleteTransaction=true)]
  public void CalcOp(Operation enmOp, int intA, int intB)
  {
    int intResult = 0;
    switch (enmOp)
    {
      case Operation.Add:
        intResult = intA + intB;
        break;
      case Operation.Subtract:
        intResult = intA - intB;
        break;
      case Operation.Multiply:
        intResult = intA * intB;
        break;
      case Operation.Divide:
        intResult = intA / intB;
        break;
      default:
        throw new Exception("Invalid operation.");
    }
    Console.WriteLine("Received {0}({1}, {2}) - result: {3}", enmOp.ToString(), intA, intB, intResult);
}

There's a AutoCompleteTransaction property set to true in OperationBehaviour attribute, which automatically commits the transaction if no exceptions are thrown.

Up till now, nothing is different from any basic Indigo service. What's beautiful about Indigo architecture is that every single binding detail can be done via configuration. MSMQ endpoint is therefore configured using the following config file:

<?xml version="1.0" encoding="utf-8" ?>
<configuration xmlns="
http://schemas.microsoft.com/.NetConfiguration/v2.0">
 <system.serviceModel>
  <bindings>
   <netProfileMsmqBinding>
    <binding configurationName = "MSMQBinding"
       msmqAuthenticationMode = "None"
       msmqProtectionLevel = "None"/>
   </netProfileMsmqBinding>
  </bindings>
  <services>
   <service serviceType = "Indigo.Demos.CalculatorService">
    <endpoint address = "net.msmq://./private$/CalcQueue"
        bindingSectionName = "netProfileMsmqBinding"
        bindingConfiguration = "MSMQBinding"
        contractType = "Indigo.Demos.CalculatorService, MSMQService"/>
   </service>
  </services>
 </system.serviceModel>
</configuration>

Setting netProfileMsmqBinding binding to this Indigo service will cause the infrastructure to listen on the specified queue (localhost/private$/CalcQueue). You must set authentication mode and protection level to none, if you're running in MSMQ workgroup mode, since by default it uses an internal certificate (from AD) - and therefore fails on other default security settings.

We can use transaction semantics on the client which will cause to submit all-or-no messages into the service queue:

ChannelFactory<IChannel> cf = new ChannelFactory<IChannel>("EndpointConfig");
ICanCalculate channel = cf.CreateChannel();
using (TransactionScope scope = new TransactionScope(TransactionScopeOption.RequiresNew))
{
  int intA = 10;
  int intB = 7;
  channel.CalcOp(Operation.Add, intA, intB);
  channel.CalcOp(Operation.Subtract, intA, intB);
  channel.CalcOp(Operation.Multiply, intA, intB);
  channel.CalcOp(Operation.Divide, intA, intB);
  scope.Complete();
}

Client configuration should look like this:

<?xml version="1.0" encoding="utf-8" ?>
<configuration xmlns="
http://schemas.microsoft.com/.NetConfiguration/v2.0">
 <system.serviceModel>
  <bindings>
   <netProfileMsmqBinding>
    <binding configurationName="MSMQBinding"
       msmqAuthenticationMode = "None"
       msmqProtectionLevel = "None"/>
   </netProfileMsmqBinding>
  </bindings>

  <client>
   <endpoint address="net.msmq://./private$/CalcQueue"
                      bindingSectionName="netProfileMsmqBinding"
                      bindingConfiguration="MSMQBinding"
       contractType="Indigo.Demos.ICanCalculate"
       configurationName="EndpointConfig"/>
        </client>
 </system.serviceModel>
</configuration>

Client and service are now totally independent. You can send arbitrary number of requests to the service side and they will land in the MSMQ queue. Your service does not need to be running at the same time your client is. When it gets back up, all existing messages will get processed automatically.

Categories:  .NET 3.0 - WCF
Tuesday, May 24, 2005 9:28:16 PM (Central Europe Standard Time, UTC+01:00)  #    Comments

 

Sunday, June 12, 2005 9:53:17 AM (Central Europe Standard Time, UTC+01:00)
Hey Matevz,

Good post. Add in another service to read in the poison queues and the dead letters and we could have something which Indigo sorely lacks --- Transactions Compensation or something which remotely resembles WS-BA ;)
Monday, August 8, 2005 1:02:52 AM (Central Europe Standard Time, UTC+01:00)
Hi Matevž.

I only wish I have foud your post 4 hours earlier... The lines:

msmqAuthenticationMode = "None"
msmqProtectionLevel = "None"

really gave me hard time. But, looking from the bright side, at least I learned something... :-)

Maybe just a word about the symptoms if one omits the above configuration. The send operation on the client side apparently socceeds (i.e. exception is not thrown), but it never reaches the server side. Moreover, even the message is not recorded in the queue, nor sent to a dead letter queue. One large, pure nothing... Very frustrating!

Oh, and if someone wonders how to work with non-transactional queues - there is a setting:

&ltbinding
configurationName="MSMQBinding"
assurances="None" | "ExactlyOnce"
/&gt

"None" works with nontransactional queues and "ExcatlyOnce" with transactional ones.
Tadej Mali
Thursday, June 21, 2007 7:35:27 PM (Central Europe Standard Time, UTC+01:00)
Hi,

How to get the message arrival time onto the queue (from the service's methods if possible) ?
Alain Trépanier
Thursday, June 21, 2007 7:57:11 PM (Central Europe Standard Time, UTC+01:00)
You have a property on the Message object. Can you elaborate on what you mean by "from the service's methods"?
All comments require the approval of the site owner before being displayed.
Name
E-mail
Home page

Comment (HTML not allowed)  

Enter the code shown (prevents robots):

Live Comment Preview
Copyright © 2003-2019 , Matevž Gačnik
Recent Posts
RD / MVP
Feeds
RSS: Atom:
Archives
Categories
Blogroll
Legal

The opinions expressed herein are my own personal opinions and do not represent my company's view in any way.

My views often change.

This blog is just a collection of bytes.

Copyright © 2003-2019
Matevž Gačnik

Send mail to the author(s) E-mail