Custom Demux

This sample demonstrates how MSMQ message headers can be mapped to different service operations so that Windows Communication Foundation (WCF) services that use MsmqIntegrationBinding are not limited to using one service operation as demonstrated in the Message Queuing to Windows Communication Foundation and Windows Communication Foundation to Message Queuing samples.

The service in this sample is a self-hosted console application to enable you to observe the service that receives queued messages.

The service contract is IOrderProcessor, and defines a one-way service that is suitable for use with queues.

[ServiceContract]
[KnownType(typeof(PurchaseOrder))]
[KnownType(typeof(String))]
public interface IOrderProcessor
{
    [OperationContract(IsOneWay = true, Name = "SubmitPurchaseOrder")]
    void SubmitPurchaseOrder(MsmqMessage<PurchaseOrder> msg);

    [OperationContract(IsOneWay = true, Name = "CancelPurchaseOrder")]
    void CancelPurchaseOrder(MsmqMessage<string> ponumber);
}

An MSMQ message does not have an Action header. It is not possible to map different MSMQ messages to operation contracts automatically. Therefore, there can be only one operation contract. To overcome this limitation, the service implements the SelectOperation method of the IDispatchOperationSelector interface. The SelectOperation method enables the service to map a given header of the message to a particular service operation. In this sample, the message's label header is mapped to the service operations. The Name parameter of the operation contract determines which service operation must be dispatched for a given message label. For example, if the message's label header contains "SubmitPurchaseOrder", the "SubmitPurchaseOrder" service operation is invoked.

public class OperationSelector : IDispatchOperationSelector
{
    public string SelectOperation(ref System.ServiceModel.Channels.Message message)
    {
        MsmqIntegrationMessageProperty property = MsmqIntegrationMessageProperty.Get(message);
        return property.Label;
    }
}

The service must implement the ApplyDispatchBehavior method of the IContractBehavior interface as shown in the following sample code. This applies the custom OperationSelector to the service framework dispatch runtime.

void IContractBehavior.ApplyDispatchBehavior(ContractDescription description, ServiceEndpoint endpoint, DispatchRuntime dispatch)
{
    dispatch.OperationSelector = new OperationSelector();
}

A message must pass through the dispatcher's ContractFilter before getting to the OperationSelector. By default a message is rejected if its action cannot be found on any contract implemented by the service. To avoid this check, we implement an IEndpointBehavior named MatchAllFilterBehavior, which allows any message to pass through the ContractFilter by applying the MatchAllMessageFilter as follows.

public void ApplyDispatchBehavior(ServiceEndpoint serviceEndpoint, EndpointDispatcher endpointDispatcher)
{
    endpointDispatcher.ContractFilter = new MatchAllMessageFilter();
}

When a message is received by the service, the appropriate service operation is dispatched using the information provided by the label header. The body of the message is deserialized into a PurchaseOrder object, as shown in the following sample code.

[OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = true)]
public void SubmitPurchaseOrder(MsmqMessage<PurchaseOrder> msg)
{
    PurchaseOrder po = (PurchaseOrder)msg.Body;
    Random statusIndexer = new Random();
    po.Status = (OrderStates)statusIndexer.Next(3);
    Console.WriteLine("Processing {0} ", po);
}

The service is self-hosted. When using the MSMQ, the queue that is used must be created in advance. This can be done manually or through code. In this sample, the service contains code to check for the existence of the queue and creates it if the queue does not exist. The queue name is read from the configuration file.

public static void Main()
{
    // Get MSMQ queue name from app settings in configuration
    string queueName = ConfigurationManager.AppSettings["orderQueueName"];

    // Create the transacted MSMQ queue if necessary.
    if (!MessageQueue.Exists(queueName))
        MessageQueue.Create(queueName, true);

    // Create a ServiceHost for the CalculatorService type.
    using (ServiceHost serviceHost = new ServiceHost(typeof(OrderProcessorService)))
    {               
        ServiceEndpoint endpoint = serviceHost.Description.Endpoints[0];
        endpoint.Behaviors.Add(new MatchAllFilterBehavior());

        //Open the ServiceHost to create listeners and start listening for messages.
        serviceHost.Open();

        // The service can now be accessed.
        Console.WriteLine("The service is ready.");
        Console.WriteLine("Press <ENTER> to terminate service.");
        Console.ReadLine();

        // Close the ServiceHost to shutdown the service.
        serviceHost.Close();
    }
}

The MSMQ queue name is specified in an appSettings section of the configuration file.

Note

The queue name uses a dot (.) for the local computer and backslash separators in its path. The WCF endpoint address specifies a msmq.formatname scheme, and uses localhost for the local computer. What follows the scheme is a properly formatted queue address according to the MSMQ Format name addressing guidelines.

<appSettings>
    <!-- Use appSetting to configure the MSMQ queue name. -->
    <add key="queueName" value=".\private$\Orders" />
</appSettings>

Note

This sample requires the installation of Message Queuing.

Start the service and run the client.

The following output is seen on the client.

Placed the order:Purchase Order: 28fc457a-1a56-4fe0-9dde-156965c21ed6
        Customer: somecustomer.com
        OrderDetails
                Order LineItem: 54 of Blue Widget @unit price: $29.99
                Order LineItem: 890 of Red Widget @unit price: $45.89
        Total cost of this order: $42461.56
        Order status: Pending
Canceled the Order: 28fc457a-1a56-4fe0-9dde-156965c21ed6
Press <ENTER> to terminate client.

The following output must be seen on the service.

The service is ready.
Press <ENTER> to terminate service.
Processing Purchase Order: 28fc457a-1a56-4fe0-9dde-156965c21ed6
        Customer: somecustomer.com
        OrderDetails
                Order LineItem: 54 of Blue Widget @unit price: $29.99
                Order LineItem: 890 of Red Widget @unit price: $45.89
        Total cost of this order: $42461.56
        Order status: Shipped
Purchase Order 28fc457a-1a56-4fe0-9dde-156965c21ed6 is canceled

To set up, build, and run the sample

  1. Ensure that you have performed the One-Time Setup Procedure for the Windows Communication Foundation Samples.

  2. If the service is run first, it will check to ensure that the queue is present. If the queue is not present, the service will create one. You can run the service first to create the queue, or you can create one via the MSMQ Queue Manager. Follow these steps to create a queue in Windows 2008.

    1. Open Server Manager in Visual Studio 2010.

    2. Expand the Features tab.

    3. Right-click Private Message Queues, and select New, Private Queue.

    4. Check the Transactional box.

    5. Enter ServiceModelSamplesTransacted as the name of the new queue.

  3. To build the C# or Visual Basic .NET edition of the solution, follow the instructions in Building the Windows Communication Foundation Samples.

  4. To run the sample in a single- or cross- computer configuration, follow the instructions in Running the Windows Communication Foundation Samples.

To run the sample across computers

  1. Copy the service program files from the \service\bin\ folder, under the language-specific folder, to the service computer.

  2. Copy the client program files from the \client\bin\ folder, under the language-specific folder, to the client computer.

  3. In the Client.exe.config file, change the orderQueueName to specify the service computer name instead of ".".

  4. On the service computer, launch Service.exe from a command prompt.

  5. On the client computer, launch Client.exe from a command prompt.

ms752265.Important(en-us,VS.100).gif Note:
The samples may already be installed on your computer. Check for the following (default) directory before continuing.

<InstallDrive>:\WF_WCF_Samples

If this directory does not exist, go to Windows Communication Foundation (WCF) and Windows Workflow Foundation (WF) Samples for .NET Framework 4 to download all Windows Communication Foundation (WCF) and WF samples. This sample is located in the following directory.

<InstallDrive>:\WF_WCF_Samples\WCF\Basic\Binding\MSMQIntegration\CustomDemux

See Also

Concepts

Queuing in WCF

Other Resources

Message Queuing