The calling code uses a transaction that is flowed to the service, but the service implementation does not actually participate in that transaction. From the client’s perspective, the transaction is completed once the message is successfully transmitted to the remote queue. MSMQ then starts another transaction when the message is delivered from the queue to the service. The service participates in this second transaction. Of course, this assumes that a transactional queue is being used.

The interaction between MSMQ and the target service, as it relates to transactions, essentially guarantees that no message will ever be lost.

MSMQ Failure Scenarios

You must be prepared to deal with two important failure scenarios in regards to MSMQ:

The inability to deliver a message from MSMQ to the target WCF service

The inability of the WCF service to process the message

In the first scenario, the message cannot be transferred from the queue to the service. In the second, the WCF service accepts the message but is unable to process it (i.e., it throws an exception). This is an important distinction because the first scenario describes a non-dispatchable or "dead" message (one that could not be delivered) and the second describes a "poison" message (one that could not be processed by the target service). You can handle dead and poison messages differently.

WCF offers automatic retry logic for non-dispatchable or poison messages. This retry logic can be configured within the binding as follows:

Notice the mexRetryCycles, retryCycleDelay, and receiveRetryCount attributes on the binding element. Upon the first message failure, MSMQ retires the delivery until the receiveRetryCount is surpassed, and then the message is moved into a retry sub queue. The system then waits at least the amount of time specified in the retryCycleDelay and attempts to deliver the message again, up to the number of times indicated in the receiveRetryCount. This entire pattern will repeat itself up to the number of times indicated in maxRetryCycles. If the maxRetryCycles is surpassed or the message is older than the value indicated in the timeToLive setting, the message is moved into the dead letter queue. By default, the system dead letter queue is used but you can also configure a custom dead letter queue as follows:

While non-dispatchable messages are moved into the dead letter queue, poison messages are handled a little differently. After the retry logic has been exhausted, the poison message is routed according to the setting indicated in the receiveErrorHandling attribute:

<binding name="netMsmq" receiveErrorHandling="Fault">

Table 1 outlines the possible values for the receiveErrorHandling attribute.

Table 1. Values for the receiveErrorHandling Attribute

Setting

Description

Drop

The message will simply be removed and ignored. The message is lost.

Fault

A fault is sent to the queue manager. No further messages will be processed until the faulted message is removed from the queue.

Move

The message will be moved into a sub-queue named "poison." (Available in MSMQ 4 only)

Reject

A rejection is sent to the queue manager and the message is placed into the dead letter queue. (Available in MSMQ 4 only)

Notice that "Move" and "Reject" are available only in MSQM 4, which ships with Windows Vista and Windows Server 2008. If receiveErrorHandling is set to "Move," WCF will automatically create a poison sub-queue and move the message into it.

Table 2 provides a quick overview of the attributes you can use to configure NetMsmqBinding. It is by no means a complete list; it merely reviews the key points covered so far.

Table 2. Overview of Settings for NetMsmqBinding

Setting

Description

receiveErrorHandling

Tells WCF/MSMQ how to deal with poison messages. The default value is "Fault."

receiveRetryCount

Indicates how many times a poison or non-dispatchable message should be retired after an initial failure. The default value is 5.

maxRetryCycles

Default is 2.

timeToLive

Indicates the maximum amount of time a message can sit in a queue waiting to be delivered to a target service. This value supersedes all retry logic. The default value is 1 day.

deadLetterQueue

Informs WCF/MSMQ how dead messages should be handled. The default value is "System."

customDeadLetterQueue

Targets an application-specific queue to hold dead messages. The default is "null."

exactlyOnce

Indicates that each message should be processed only one time. The default value is "true."

The Benefits of WCF and MSMQ Integration

Adding MSMQ via the NetMsmqBinding to your WCF-based service does add complexity, but any challenges from that complexity are greatly outweighed by the benefits:

Increased robustness

Decreased coupling

Message durability

Greater scalability

All these can be had with little or no additional code. This is another example of how WCF makes leveraging the features of the platform simple and frees the developer to concentrate on the business.

Steve Stefanovich has spent over a decade with RDA Corporation as a developer and principal architect focusing on custom application development using Microsoft .Net and SharePoint technologies. Steve is a frequent contributor to RDA's Architecture and Collaboration blogs.