public MessageSender(AzureServiceBusSettings serviceBusSettings, ServiceBusConnection serviceBusConnection, NamespaceInfo namespaceInfo, MessageProcessingContext context, string destinationQueue) { _messageProcessingContext = context; _settings = serviceBusSettings; _namespaceInfo = namespaceInfo; // The default retry policy passed in to the sender provides an exponential backoff for transient failures. if (context == null) { _messageSender = new Microsoft.Azure.ServiceBus.Core.MessageSender(serviceBusConnection, destinationQueue, RetryPolicy.Default); } else { var viaQueue = context.GetMessageReceiver().Path; if (viaQueue == destinationQueue) { _messageSender = new Microsoft.Azure.ServiceBus.Core.MessageSender(serviceBusConnection, destinationQueue, RetryPolicy.Default); } else { _messageSender = new Microsoft.Azure.ServiceBus.Core.MessageSender(serviceBusConnection, destinationQueue, viaQueue, RetryPolicy.Default); } } }
private async Task InnerMessageHandler(Message message, Message controlMessage, CancellationToken ct) { byte[] messageBody = null; // Check if this is a large message that we need to download the payload for if (message.UserProperties.TryGetValue(Constants.HeaderKeys.RPBlobSizeBytes, out var blobSizeBytes)) { messageBody = await DownloadMessageBodyFromBlob(message.MessageId, long.Parse(blobSizeBytes.ToString())).ConfigureAwait(false); } else { messageBody = message.Body; } // Deserialize if (message.UserProperties.TryGetValue(Constants.HeaderKeys.RPMessageType, out var assemblyQualifiedNameObject)) { var type = Type.GetType(assemblyQualifiedNameObject.ToString()); IMessageHandler handler = _messageHandlerFactory.CreateMessageHandler(type); object deserializedMessage; try { var messageString = Encoding.UTF8.GetString(messageBody); deserializedMessage = JsonConvert.DeserializeObject(messageString, type); } catch (Exception ex) { await _messageReceiver.DeadLetterAsync(message.SystemProperties.LockToken, $"Error deserializing message body: {ex}") .ConfigureAwait(false); return; } // Determine if we need to use a transaction for this handler - check the "Transactional" attribute // on the handler class var useTransaction = true; var transactionalAttributes = handler.GetType().GetCustomAttributes(typeof(TransactionalAttribute), true); if (transactionalAttributes == null || transactionalAttributes.Length == 0) { useTransaction = false; } var context = new MessageProcessingContext(_settings, _messageReceiver, message, controlMessage, _messagingFactory, useTransaction); using (var tx = useTransaction ? new TransactionScope(TransactionScopeAsyncFlowOption.Enabled) : null) { try { await handler.HandleMessage(deserializedMessage, context).ConfigureAwait(false); } catch (Exception ex) { // If an exception was thrown on the last attempt, explicitly dead-letter the message. // If we don't do this, ASB will dead letter the message with a DeadLetterReason field // value of MaxDeliveryCountExceeded, which is not particularly useful for diagnosis. // Instead, we explicitly dead-letter the message with some details of the exception that // was thrown. if (message.SystemProperties.DeliveryCount == _queueDescription.MaxDeliveryCount) { var deadLetterReason = ""; var deadLetterDescription = ""; GetDeadLetterFields(ex, out deadLetterReason, out deadLetterDescription); await context.DeadLetter(deadLetterReason, deadLetterDescription).ConfigureAwait(false); } throw; } if (!context.IsAbandoned) { tx?.Complete(); } } if (context.IsAbandoned) { await _messageReceiver.AbandonAsync(message.SystemProperties.LockToken).ConfigureAwait(false); if (controlMessage != null) { await _messageReceiver.AbandonAsync(controlMessage.SystemProperties.LockToken).ConfigureAwait(false); } } } else { await _messageReceiver.DeadLetterAsync(message.SystemProperties.LockToken, $"Message has no type specified in the {Constants.HeaderKeys.RPMessageType} header, cannot deserialize message.") .ConfigureAwait(false); return; } }