I have a Windows Service that monitors a private MSMQ for messages by providing a handler for the MSMQ peekCompleted event, like this (assigned during service OnStart):
_queue.PeekCompleted += new PeekCompletedEventHandler(MessageReceived);
The MessageReceived method then does this (variables beginning with an underscore are declared globally in the service):
private void MessageReceived(object sender, PeekCompletedEventArgs e)
{
try
{
_handler = new MessageHandler(); //does the heavy lifting
_queueMessage = _queue.EndPeek(e.AsyncResult);
_queueMessage.Formatter = new BinaryMessageFormatter();
_currentMessage = (MyMessageType)_queueMessage.Body;
// we have the message so no matter what we want it off of the queue
_queue.ReceiveById(_queueMessage.Id);
// Message Handler is going to swallow everything here
// messages are processed OK or moved to a 'failed' or 'poison' queue
// there should be no uncaught exceptions
_handler.Process(_currentMessage);
//go wait again
_queue.BeginPeek();
}
catch (Exception exception)
{
// here is question
}
}
Question: given that _handler swallows any exceptions it generates/encounters (and deals with its message accordingly) I wouldn't expect exceptions in this try block but if any do happen I need to ensure that the queue is returned to the proper state, even tho I have no idea where the exception might have occurred. So do I just
_queue.ReceiveById(_queueMessage.Id);
_queue.BeginPeek();
in the catch block even though either of those two statements might either have already been executed? I can't really assume (ahem) that one of them is not generating the exception for some unknown reason, that being the nature of exceptions, yes?
How should this be handled?
Don't handle it. If you make your queues transactional then you can wrap you ReceiveById call in a transaction.:
using (TransactionScope txnScope = new TransactionScope())
{
_queue.ReceiveById(_queueMessage.Id);
txnScope.Commit();
}
This will ensure that if an exception is thrown before the commit the message will be left on the queue. This will only work if the queue you are receiving from is local. Otherwise you'll need to configure DTC on all the machines involved.