Non-Destructive Queue Receive

You should take the time to understand the earlier articles in the series for context if you haven't already.

The three basic operations that we talked about for queuing with non-destructive receives are peek, lock, and delete. Rather than creating a new channel shape for peeked receives, you continue to use the same IInputChannel interfaces. The binding is configured such that either every receive through the channel is a destructive receive or every receive through the channel is a non-destructive receive. There is no option for a mixed mode or optional behavior.

When a message is read non-destructively, the queue channel attaches a ReceiveContext message property to the message that represents the lock. You never see a message until peek and lock have executed.

 public abstract class ReceiveContext
{
    public readonly static string Name = "ReceiveContext";

    protected ReceiveContext();

    public static bool TryGet(Message message, out ReceiveContext property);
    public static bool TryGet(MessageProperties properties, out ReceiveContext property);

    public ReceiveContextState State
    {
        get; protected set;
    }

    protected object ThisLock
    {
        get;
    }

    public virtual void Abandon(TimeSpan timeout);
    public virtual IAsyncResult BeginAbandon(TimeSpan timeout, AsyncCallback callback, object state);
    protected abstract void OnAbandon(TimeSpan timeout);
    protected abstract void OnEndAbandon(IAsyncResult result);
    protected abstract IAsyncResult OnBeginAbandon(TimeSpan timeout, AsyncCallback callback, object state);
    public virtual void EndAbandon(IAsyncResult result);

    public virtual void Complete(TimeSpan timeout);
    public virtual IAsyncResult BeginComplete(TimeSpan timeout, AsyncCallback callback, object state);
    protected abstract IAsyncResult OnBeginComplete(TimeSpan timeout, AsyncCallback callback, object state);
    protected abstract void OnComplete(TimeSpan timeout);
    protected abstract void OnEndComplete(IAsyncResult result);
    public virtual void EndComplete(IAsyncResult result);

    protected virtual void Fault();
    protected virtual void OnFaulted();
}

public enum ReceiveContextState
{
    Received,
    Completing,
    Completed,
    Abandoning,
    Abandoned,
    Faulted
}

What you do get to decide is whether the non-destructive receive should be converted into a destructive receive. There are two methods on the ReceiveContext that control the receive.

The Abandon method reverts the non-destructive receive and unlocks the message. This is an immediate action and precludes further attempts at processing the message until it is read again.

The Complete method converts the non-destructive receive to a destructive receive. If there is an ambient transaction, then the Complete method will execute in the context of that transaction. Should the transaction roll back, the destructive receive will revert back to a non-destructive receive with you continuing to hold the lock on the message.