Share via


Custom Demux

Download sample

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 MSMQ to Windows Communication Foundation and Windows Communication Foundation To MSMQ 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.

NoteNote:

The queue name uses a dot (.) for the local machine and backslash separators in its path. The WCF endpoint address specifies a msmq.formatname scheme, and uses localhost for the local machine. 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>
NoteNote:

This sample requires the installation of Message Queuing. See Message Queuing for installation instructions.

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
Cancelled 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 cancelled

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. To build the C# or Visual Basic .NET edition of the solution, follow the instructions in Building the Windows Communication Foundation Samples.

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

To run the sample across machines

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

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

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

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

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

See Also

Other Resources

Queuing in WCF
Message Queuing

Footer image

Send comments about this topic to Microsoft.
© Microsoft Corporation. All rights reserved.