I have a poisoned message service. When I receive a poisoned message, the error is handled by a poisoned message handler, which sends an email, which then moves the message off of the queue. the problem I'm having is rather than sending one email. It sends 4-5 and I'm really struggling to figure out why this is happening. The way I do this is check the inner exception of the poisoned message exception, which should be a strongly typed exception. that contains a fault contract that I use to send an email notification.
Below is how I have everything set up. Hope its not too convoluted and is clear.
This is the binding config.
<?xml version="1.0" encoding="utf-8" ?>
<bindings>
<netMsmqBinding>
<binding name="BindingConfiguration"
maxRetryCycles="1"
receiveErrorHandling="Fault"
receiveRetryCount="0"
retryCycleDelay="00:00:30">
<security mode="None"/>
</binding>
</netMsmqBinding>
</bindings>
When the poison message service receives a poisoned message it will have give the message another try. If that fails, I throw an msmqpoisonedmessageexception, of I add an inner exception, which is the strongly typed fault contract.
<OperationBehavior(TransactionScopeRequired:=True, TransactionAutoComplete:=True)>
Public Sub SaveEmail(ByVal newEmail As Contracts.Contact, ByVal oldEmail As Contracts.Contact, ByVal emailType As Contracts.EmailType, ByVal settings As Contracts.Configuration) Implements Contracts.IService.SaveEmail
If Validation.ValidateEmail(newEmail) Then
Try
_repository.SaveEmail(newEmail, oldEmail, emailType)
If Not settings.AuditNote Is Nothing Then
_repository.WriteClientNote(settings.AuditNote)
End If
Catch fe As FaultException(Of ContactFault)
Throw New MsmqPoisonMessageException(fe.Message, fe)
Catch ex As Exception
Dim contactFault As New ContactFault With {.Message = ex.Message, .NewContact = newEmail, .OldContact = oldEmail, .StackTrace = ex.StackTrace.ToString}
Throw New MsmqPoisonMessageException(ex.Message, New FaultException(Of ContactFault)(contactFault))
End Try
End If
End Sub
The exception is then handled in a poison message exception handler class. Which has two main methods :-
The one below checks wether the error is an msmqpoisonedmessageexception if it is , it will pass it to the next method to be removed.
1)
Public Function HandleError(ByVal [error] As System.Exception) As Boolean Implements System.ServiceModel.Dispatcher.IErrorHandler.HandleError
poisonException = TryCast([error], MsmqPoisonMessageException)
If poisonException IsNot Nothing Then
Try
lookupID = poisonException.MessageLookupId
Catch ex As Exception
EventLog.WriteEntry(My.Application.Info.ProductName, ex.Message & "Look up", EventLogEntryType.Warning)
End Try
Queue = New MessageQueue(ConfigurationManager.AppSettings("QueueName"))
PoisonQueue = New MessageQueue(ConfigurationManager.AppSettings("PoisonQueueName"))
RemovePoisonedMessage()
Return True
End If
Return False
End Function
this next method takes the poisoned messag exception and rmeoves it from the queue, and adds it to a deadmessagequeue. It also checks the type of fault and uses the corresponding method for the corresponding type of fault.
Protected Overrides Sub RemovePoisonedMessage()
If TypeOf poisonException.InnerException Is FaultException(Of AddressFault) Then
Email_AddressValidationFault(poisonException.InnerException)
ElseIf TypeOf poisonException.InnerException Is FaultException(Of ContactFault) Then
Email_ContactValidationFault(poisonException.InnerException)
ElseIf TypeOf poisonException.InnerException Is FaultException(Of DebtFault) Then
Email_DebtValidationFault(poisonException.InnerException)
End If
Dim message As System.Messaging.Message = Nothing
Using txScope As New TransactionScope(TransactionScopeOption.RequiresNew)
Dim retryCount As Int32 = 0
While retryCount < 3
retryCount += 1
Try
message = MyBase.Queue.ReceiveByLookupId(MessageLookupAction.Current, MyBase.lookupID, MessageQueueTransactionType.Automatic)
If message IsNot Nothing Then
message.UseDeadLetterQueue = True
Try
MyBase.PoisonQueue.Send(message, System.Messaging.MessageQueueTransactionType.Automatic)
Catch ex As Exception
EventLog.WriteEntry(My.Application.Info.ProductName, ex.Message, EventLogEntryType.Error)
End Try
txScope.Complete()
'
Return
End If
Catch generatedExceptionName As InvalidOperationException
End Try
End While
End Using
End Sub