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.
- Indigo Runtime RC Beta 1
- WinFX SDK RC Beta 1
- 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.