Esempio n. 1
0
        public async Task CustomMessageHandler_WithContextFilter_UsesMessageTypeDuringSelection()
        {
            // Arrange
            var spyHandler = new StubTestMessageHandler <Purchase, MessageContext>();

            var collection = new MessageHandlerCollection(new ServiceCollection());

            collection.WithMessageHandler <StubTestMessageHandler <Order, MessageContext>, Order>();
            collection.WithMessageHandler <StubTestMessageHandler <Purchase, TestMessageContext>, Purchase, TestMessageContext>();
            collection.WithMessageHandler <StubTestMessageHandler <Purchase, MessageContext>, Purchase>(provider => spyHandler);

            IServiceProvider serviceProvider = collection.Services.BuildServiceProvider();
            var router = new TestMessageRouter(serviceProvider, _logger);

            var purchase = new Purchase
            {
                CustomerName = _bogusGenerator.Name.FullName(),
                Price        = _bogusGenerator.Commerce.Price()
            };
            string purchaseJson    = JsonConvert.SerializeObject(purchase);
            var    context         = new MessageContext("message-id", new Dictionary <string, object>());
            var    correlationInfo = new MessageCorrelationInfo("operation-id", "transaction-id");

            // Act
            await router.RouteMessageAsync(purchaseJson, context, correlationInfo, CancellationToken.None);

            // Assert
            Assert.True(spyHandler.IsProcessed);
        }
Esempio n. 2
0
        /// <summary>
        /// Handle the original <paramref name="message"/> that was received through an registered <see cref="IFallbackMessageHandler"/>.
        /// </summary>
        /// <param name="message">The message that was received.</param>
        /// <param name="messageContext">The context providing more information concerning the processing.</param>
        /// <param name="correlationInfo">The information concerning correlation of telemetry and processes by using a variety of unique identifiers.</param>
        /// <param name="cancellationToken">The token to cancel the message processing.</param>
        /// <exception cref="ArgumentNullException">
        ///     Thrown when the <paramref name="messageContext"/>, or <paramref name="correlationInfo"/> is <c>null</c>.
        /// </exception>
        /// <returns>
        ///     [true] if the received <paramref name="message"/> was handled by the registered <see cref="IFallbackMessageHandler"/>; [false] otherwise.
        /// </returns>
        protected async Task <bool> TryFallbackProcessMessageAsync <TMessageContext>(
            string message,
            TMessageContext messageContext,
            MessageCorrelationInfo correlationInfo,
            CancellationToken cancellationToken)
            where TMessageContext : MessageContext
        {
            Guard.NotNull(messageContext, nameof(messageContext), "Requires a message context to send to the message handler");
            Guard.NotNull(correlationInfo, nameof(correlationInfo), "Requires correlation information to send to the message handler");

            if (HasFallbackMessageHandler)
            {
                string fallbackMessageHandlerTypeName = _fallbackMessageHandler.Value.GetType().Name;

                Logger.LogTrace("Fallback on registered '{FallbackMessageHandlerType}' because none of the message handlers were able to process the message", fallbackMessageHandlerTypeName);

                Task processMessageAsync = _fallbackMessageHandler.Value.ProcessMessageAsync(message, messageContext, correlationInfo, cancellationToken);
                if (processMessageAsync is null)
                {
                    throw new InvalidOperationException(
                              $"Cannot fallback upon the fallback message handler '{fallbackMessageHandlerTypeName}' "
                              + "because the handler was not correctly implemented to process the message as it returns 'null' for its asynchronous operation");
                }

                await processMessageAsync;
                Logger.LogTrace("Fallback message handler '{FallbackMessageHandlerType}' has processed the message", fallbackMessageHandlerTypeName);

                return(true);
            }

            return(false);
        }
Esempio n. 3
0
        public async Task CustomMessageHandlerFactory_WithCustomContext_SubtractsRegistration()
        {
            // Arrange
            var spyHandler = new TestMessageHandler();

            var collection = new MessageHandlerCollection(new ServiceCollection());

            collection.WithMessageHandler <TestMessageHandler, TestMessage, TestMessageContext>(provider => spyHandler);
            IServiceProvider serviceProvider = collection.Services.BuildServiceProvider();

            // Act
            IEnumerable <MessageHandler> messageHandlers = MessageHandler.SubtractFrom(serviceProvider, _logger);

            // Assert
            MessageHandler messageHandler = Assert.Single(messageHandlers);

            Assert.NotNull(messageHandler);

            var message         = new TestMessage();
            var context         = TestMessageContext.Generate();
            var correlationInfo = new MessageCorrelationInfo("operation-id", "transaction-id");
            await messageHandler.ProcessMessageAsync(message, context, correlationInfo, CancellationToken.None);

            Assert.True(spyHandler.IsProcessed);
        }
Esempio n. 4
0
        /// <summary>
        /// Handle a new <paramref name="message"/> that was received by routing them through registered <see cref="IMessageHandler{TMessage,TMessageContext}"/>s
        /// and optionally through an registered <see cref="IFallbackMessageHandler"/> if none of the message handlers were able to process the <paramref name="message"/>.
        /// </summary>
        /// <param name="message">The message that was received.</param>
        /// <param name="messageContext">The context providing more information concerning the processing.</param>
        /// <param name="correlationInfo">The information concerning correlation of telemetry and processes by using a variety of unique identifiers.</param>
        /// <param name="cancellationToken">The token to cancel the message processing.</param>
        /// <exception cref="ArgumentNullException">
        ///     Thrown when the <paramref name="message"/>, <paramref name="messageContext"/>, or <paramref name="correlationInfo"/> is <c>null</c>.
        /// </exception>
        /// <exception cref="InvalidOperationException">Thrown when no message handlers or none matching message handlers are found to process the message.</exception>
        public virtual async Task RouteMessageAsync <TMessageContext>(
            string message,
            TMessageContext messageContext,
            MessageCorrelationInfo correlationInfo,
            CancellationToken cancellationToken)
            where TMessageContext : MessageContext
        {
            Guard.NotNull(message, nameof(message), "Requires message content to deserialize and process the message");
            Guard.NotNull(messageContext, nameof(messageContext), "Requires a message context to send to the message handler");
            Guard.NotNull(correlationInfo, nameof(correlationInfo), "Requires correlation information to send to the message handler");

            bool isProcessed = await TryProcessMessageAsync(message, messageContext, correlationInfo, cancellationToken);

            if (isProcessed)
            {
                return;
            }

            bool isFallbackProcessed = await TryFallbackProcessMessageAsync(message, messageContext, correlationInfo, cancellationToken);

            if (!isFallbackProcessed)
            {
                throw new InvalidOperationException(
                          $"Message pump cannot correctly process the message in the '{typeof(TMessageContext).Name}' "
                          + "because none of the registered 'IMessageHandler<,>' implementations in the dependency injection container matches the incoming message type and context. "
                          + $"Make sure you call the correct '.With...' extension on the {nameof(IServiceCollection)} during the registration of the message pump or message router to register a message handler");
            }
        }
 public OrderCreatedEventData(string id, int amount, string articleNumber, MessageCorrelationInfo correlationInfo)
 {
     Id              = id;
     Amount          = amount;
     ArticleNumber   = articleNumber;
     CorrelationInfo = correlationInfo;
 }
Esempio n. 6
0
        private async Task <bool> TryProcessMessageAsync <TMessageContext>(
            string message,
            TMessageContext messageContext,
            MessageCorrelationInfo correlationInfo,
            CancellationToken cancellationToken)
            where TMessageContext : MessageContext
        {
            MessageHandler[] handlers = GetRegisteredMessageHandlers().ToArray();
            if (handlers.Length <= 0 && _fallbackMessageHandler.Value is null)
            {
                throw new InvalidOperationException(
                          $"Message pump cannot correctly process the message in the '{typeof(TMessageContext).Name}' "
                          + "because no 'IMessageHandler<,>' was registered in the dependency injection container. "
                          + $"Make sure you call the correct '.With...' extension on the {nameof(IServiceCollection)} during the registration of the message pump or message router to register a message handler");
            }

            foreach (MessageHandler handler in handlers)
            {
                MessageResult result = await DeserializeMessageForHandlerAsync(message, messageContext, handler);

                if (result.IsSuccess)
                {
                    await handler.ProcessMessageAsync(result.DeserializedMessage, messageContext, correlationInfo, cancellationToken);

                    return(true);
                }
            }

            return(false);
        }
Esempio n. 7
0
        public async Task WithServiceBusRouting_WithMessageHandlerMessageBodySerializerSubType_DeserializesCustomMessage()
        {
            // Arrange
            var services       = new ServiceCollection();
            var collection     = new ServiceBusMessageHandlerCollection(services);
            var ignoredHandler = new StubServiceBusMessageHandler <TestMessage>();
            var spyHandler     = new StubServiceBusMessageHandler <Order>();

            var expectedMessage = new TestMessage {
                TestProperty = "Some value"
            };
            string expectedBody = JsonConvert.SerializeObject(expectedMessage);
            var    serializer   = new TestMessageBodySerializer(expectedBody, new SubOrder());

            collection.WithServiceBusMessageHandler <StubServiceBusMessageHandler <Order>, Order>(messageBodySerializer: serializer, implementationFactory: serviceProvider => spyHandler)
            .WithServiceBusMessageHandler <StubServiceBusMessageHandler <TestMessage>, TestMessage>(implementationFactory: serviceProvider => ignoredHandler);

            // Act
            services.AddServiceBusMessageRouting();

            // Assert
            IServiceProvider provider = services.BuildServiceProvider();
            var router = provider.GetRequiredService <IAzureServiceBusMessageRouter>();
            AzureServiceBusMessageContext context = AzureServiceBusMessageContextFactory.Generate();
            var correlationInfo = new MessageCorrelationInfo("operation-id", "transaction-id");

            ServiceBusReceivedMessage message = expectedMessage.AsServiceBusReceivedMessage();
            await router.RouteMessageAsync(message, context, correlationInfo, CancellationToken.None);

            Assert.True(spyHandler.IsProcessed);
            Assert.False(ignoredHandler.IsProcessed);
        }
Esempio n. 8
0
        /// <summary>
        /// Tries to process the unhandled <paramref name="message"/> through an potential registered <see cref="IAzureServiceBusFallbackMessageHandler"/> instance.
        /// </summary>
        /// <param name="messageReceiver">
        ///     The instance that can receive Azure Service Bus <see cref="ServiceBusReceivedMessage"/>; used within <see cref="IAzureServiceBusFallbackMessageHandler"/>s with Azure Service Bus specific operations.
        /// </param>
        /// <param name="message">The message that was received by the <paramref name="messageReceiver"/>.</param>
        /// <param name="messageContext">The context in which the <paramref name="message"/> should be processed.</param>
        /// <param name="correlationInfo">The information concerning correlation of telemetry and processes by using a variety of unique identifiers.</param>
        /// <param name="cancellationToken">The token to cancel the message processing.</param>
        /// <exception cref="ArgumentNullException">
        ///     Thrown when the <paramref name="messageReceiver"/>, <paramref name="message"/>, <paramref name="messageContext"/>, or <paramref name="correlationInfo"/> is <c>null</c>.
        /// </exception>
        protected async Task TryServiceBusFallbackMessageAsync(
            ServiceBusReceiver messageReceiver,
            ServiceBusReceivedMessage message,
            AzureServiceBusMessageContext messageContext,
            MessageCorrelationInfo correlationInfo,
            CancellationToken cancellationToken)
        {
            Guard.NotNull(message, nameof(message), "Requires an Azure Service Bus message to be processed by the registered fallback message handler");
            Guard.NotNull(messageContext, nameof(messageContext), "Requires an Azure Service Bus message context in which the incoming message can be processed");
            Guard.NotNull(correlationInfo, nameof(correlationInfo), "Requires an correlation information to correlate between incoming Azure Service Bus messages");

            if (HasAzureServiceBusFallbackHandler)
            {
                if (_fallbackMessageHandler.Value is AzureServiceBusMessageHandlerTemplate template)
                {
                    if (messageReceiver is null)
                    {
                        Logger.LogWarning("Fallback message handler '{MessageHandlerType}' uses specific Azure Service Bus operations, but is unable to be configured during message routing because the message router didn't receive a Azure Service Bus message receiver; use other '{RouteMessageAsync}' method overload",
                                          _fallbackMessageHandler.Value.GetType().Name, nameof(RouteMessageAsync));
                    }
                    else
                    {
                        var args = new ProcessMessageEventArgs(message, messageReceiver, cancellationToken);
                        template.SetProcessMessageEventArgs(args);
                    }
                }

                string fallbackMessageHandlerTypeName = _fallbackMessageHandler.Value.GetType().Name;
                Logger.LogTrace("Fallback on registered '{FallbackMessageHandlerType}' because none of the message handlers w ere able to process the message", fallbackMessageHandlerTypeName);
                await _fallbackMessageHandler.Value.ProcessMessageAsync(message, messageContext, correlationInfo, cancellationToken);

                Logger.LogTrace("Fallback message handler '{FallbackMessageHandlerType}' has processed the message", fallbackMessageHandlerTypeName);
            }
        }
        private async Task <bool> ProcessMessageAsync <TMessageContext>(
            MessageHandler handler,
            MessageReceiver messageReceiver,
            string message,
            TMessageContext messageContext,
            MessageCorrelationInfo correlationInfo,
            CancellationToken cancellationToken)
            where TMessageContext : AzureServiceBusMessageContext
        {
            bool canProcessMessage             = handler.CanProcessMessage(messageContext);
            var  messageType                   = (Type)handler.GetType().GetProperty("MessageType", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(handler);
            bool tryDeserializeToMessageFormat = TryDeserializeToMessageFormat(message, messageType, out var result);

            if (canProcessMessage && tryDeserializeToMessageFormat)
            {
                if (result is null)
                {
                    return(false);
                }

                await PreProcessingAsync(handler, messageContext, messageReceiver);

                await handler.ProcessMessageAsync(result, messageContext, correlationInfo, cancellationToken);

                return(true);
            }

            return(false);
        }
Esempio n. 10
0
        public async Task CustomMessageHandler_WithContextFilter_UsesFilterDuringSelection()
        {
            // Arrange
            var messageId = Guid.NewGuid().ToString();
            var message   = new TestMessage {
                TestProperty = Guid.NewGuid().ToString()
            };
            string messageJson     = JsonConvert.SerializeObject(message);
            var    context         = new TestMessageContext(messageId, new Dictionary <string, object>());
            var    correlationInfo = new MessageCorrelationInfo("operation-id", "transaction-id");
            var    spyHandler1     = new TestMessageHandler();
            var    spyHandler2     = new TestMessageHandler();

            var collection = new MessageHandlerCollection(new ServiceCollection());

            collection.WithMessageHandler <TestMessageHandler, TestMessage, TestMessageContext>(
                messageContextFilter: ctx => ctx.MessageId == "some other ID",
                implementationFactory: provider => spyHandler2);
            collection.WithMessageHandler <TestMessageHandler, TestMessage, TestMessageContext>(
                messageContextFilter: ctx => ctx.MessageId == messageId,
                implementationFactory: provider => spyHandler1);
            collection.WithMessageHandler <TestMessageHandler, TestMessage, TestMessageContext>();
            collection.WithMessageHandler <DefaultTestMessageHandler, TestMessage>();

            IServiceProvider serviceProvider = collection.Services.BuildServiceProvider();
            var router = new TestMessageRouter(serviceProvider, _logger);

            // Act
            await router.RouteMessageAsync(messageJson, context, correlationInfo, CancellationToken.None);

            // Assert
            Assert.True(spyHandler1.IsProcessed);
            Assert.False(spyHandler2.IsProcessed);
        }
        public async Task WithMessageRouting_WithIgnoreMissingMembers_GoesThroughDifferentMessageHandler(AdditionalMemberHandling additionalMemberHandling)
        {
            // Arrange
            var services         = new ServiceCollection();
            var collection       = new MessageHandlerCollection(services);
            var messageHandlerV1 = new OrderV1MessageHandler();
            var messageHandlerV2 = new OrderV2MessageHandler();

            collection.WithMessageHandler <OrderV1MessageHandler, Order>(provider => messageHandlerV1)
            .WithMessageHandler <OrderV2MessageHandler, OrderV2>(provider => messageHandlerV2);

            // Act
            services.AddMessageRouting(options => options.Deserialization.AdditionalMembers = additionalMemberHandling);

            // Assert
            IServiceProvider serviceProvider = services.BuildServiceProvider();
            var router = serviceProvider.GetRequiredService <IMessageRouter>();

            OrderV2 order           = OrderV2Generator.Generate();
            var     context         = new MessageContext("message-id", new Dictionary <string, object>());
            var     correlationInfo = new MessageCorrelationInfo("operation-id", "transaction-id");
            string  json            = JsonConvert.SerializeObject(order);

            await router.RouteMessageAsync(json, context, correlationInfo, CancellationToken.None);

            Assert.Equal(additionalMemberHandling is AdditionalMemberHandling.Ignore, messageHandlerV1.IsProcessed);
            Assert.Equal(additionalMemberHandling is AdditionalMemberHandling.Error, messageHandlerV2.IsProcessed);
        }
Esempio n. 12
0
        public async Task WithServiceBusRouting_IgnoreMissingMembers_ResultsInDifferentMessageHandler(AdditionalMemberHandling additionalMemberHandling)
        {
            // Arrange
            var services         = new ServiceCollection();
            var collection       = new ServiceBusMessageHandlerCollection(services);
            var messageHandlerV1 = new OrderV1AzureServiceBusMessageHandler();
            var messageHandlerV2 = new OrderV2AzureServiceBusMessageHandler();

            collection.WithServiceBusMessageHandler <OrderV1AzureServiceBusMessageHandler, Order>(provider => messageHandlerV1)
            .WithServiceBusMessageHandler <OrderV2AzureServiceBusMessageHandler, OrderV2>(provider => messageHandlerV2);

            // Act
            services.AddServiceBusMessageRouting(options => options.Deserialization.AdditionalMembers = additionalMemberHandling);

            // Assert
            IServiceProvider serviceProvider = services.BuildServiceProvider();
            var router = serviceProvider.GetRequiredService <IAzureServiceBusMessageRouter>();

            OrderV2 orderV2 = OrderV2Generator.Generate();
            ServiceBusReceivedMessage     message = orderV2.AsServiceBusReceivedMessage();
            AzureServiceBusMessageContext context = AzureServiceBusMessageContextFactory.Generate();
            var correlationInfo = new MessageCorrelationInfo("operation-id", "transaction-id");
            await router.RouteMessageAsync(message, context, correlationInfo, CancellationToken.None);

            Assert.Equal(additionalMemberHandling is AdditionalMemberHandling.Error, messageHandlerV2.IsProcessed);
            Assert.Equal(additionalMemberHandling is AdditionalMemberHandling.Ignore, messageHandlerV1.IsProcessed);
        }
Esempio n. 13
0
        public async Task WithServiceBusMessageRouting_WithMessageHandlerMessageBodyFilter_GoesThroughRegisteredMessageHandlers()
        {
            // Arrange
            var services       = new ServiceCollection();
            var collection     = new ServiceBusMessageHandlerCollection(services);
            var ignoredHandler = new StubServiceBusMessageHandler <Order>();
            var spyHandler     = new StubServiceBusMessageHandler <Order>();

            collection.WithServiceBusMessageHandler <StubServiceBusMessageHandler <Order>, Order>(
                messageBodyFilter: body => true, implementationFactory: serviceProvider => spyHandler)
            .WithServiceBusMessageHandler <StubServiceBusMessageHandler <Order>, Order>(
                messageBodyFilter: body => false, implementationFactory: serviceProvider => ignoredHandler);

            // Act
            services.AddServiceBusMessageRouting();

            // Assert
            IServiceProvider provider = services.BuildServiceProvider();
            var router = provider.GetRequiredService <IAzureServiceBusMessageRouter>();
            AzureServiceBusMessageContext context = AzureServiceBusMessageContextFactory.Generate();
            var correlationInfo = new MessageCorrelationInfo("operation-id", "transaction-id");

            Order order = OrderGenerator.Generate();
            ServiceBusReceivedMessage message = order.AsServiceBusReceivedMessage();
            await router.RouteMessageAsync(message, context, correlationInfo, CancellationToken.None);

            Assert.True(spyHandler.IsProcessed);
            Assert.False(ignoredHandler.IsProcessed);
        }
Esempio n. 14
0
        public async Task WithMessageRouting_WithMessageHandlerContextFilter_GoesThroughRegisteredMessageHandlers()
        {
            // Arrange
            var services               = new ServiceCollection();
            var collection             = new MessageHandlerCollection(services);
            var spyHandler             = new StubTestMessageHandler <TestMessage, TestMessageContext>();
            var ignoredSameTypeHandler = new StubTestMessageHandler <TestMessage, TestMessageContext>();

            var ignoredDefaultHandler = new DefaultTestMessageHandler();
            var ignoredHandler        = new TestMessageHandler();

            collection.WithMessageHandler <DefaultTestMessageHandler, TestMessage>(serviceProvider => ignoredDefaultHandler)
            .WithMessageHandler <StubTestMessageHandler <TestMessage, TestMessageContext>, TestMessage, TestMessageContext>(
                messageContextFilter: ctx => false, implementationFactory: serviceProvider => ignoredSameTypeHandler)
            .WithMessageHandler <StubTestMessageHandler <TestMessage, TestMessageContext>, TestMessage, TestMessageContext>(serviceProvider => spyHandler)
            .WithMessageHandler <TestMessageHandler, TestMessage, TestMessageContext>(serviceProvider => ignoredHandler);

            // Act
            services.AddMessageRouting();

            // Assert
            IServiceProvider provider = services.BuildServiceProvider();
            var router = provider.GetRequiredService <IMessageRouter>();

            var    correlationInfo = new MessageCorrelationInfo("operation-id", "transaction-id");
            var    context         = TestMessageContext.Generate();
            string json            = JsonConvert.SerializeObject(new TestMessage());
            await router.RouteMessageAsync(json, context, correlationInfo, CancellationToken.None);

            Assert.True(spyHandler.IsProcessed);
            Assert.False(ignoredSameTypeHandler.IsProcessed);
            Assert.False(ignoredDefaultHandler.IsProcessed);
            Assert.False(ignoredHandler.IsProcessed);
        }
        /// <summary>
        ///     Process a new message that was received
        /// </summary>
        /// <param name="message">Message that was received</param>
        /// <param name="azureMessageContext">Context providing more information concerning the processing</param>
        /// <param name="correlationInfo">
        ///     Information concerning correlation of telemetry and processes by using a variety of unique
        ///     identifiers
        /// </param>
        /// <param name="cancellationToken">Cancellation token</param>
        public override async Task ProcessMessageAsync(
            ServiceBusReceivedMessage message,
            AzureServiceBusMessageContext azureMessageContext,
            MessageCorrelationInfo correlationInfo,
            CancellationToken cancellationToken)
        {
            if (azureMessageContext.SystemProperties.DeliveryCount <= 1)
            {
                Logger.LogTrace("Abandoning message '{MessageId}'...", message.MessageId);
                await AbandonMessageAsync(message);

                Logger.LogTrace("Abandoned message '{MessageId}'", message.MessageId);
            }
            else
            {
                string json  = Encoding.UTF8.GetString(message.Body);
                var    order = JsonConvert.DeserializeObject <Order>(json);

                Logger.LogInformation("Processing order {OrderId} for {OrderAmount} units of {OrderArticle} bought by {CustomerFirstName} {CustomerLastName}",
                                      order.Id, order.Amount, order.ArticleNumber, order.Customer.FirstName, order.Customer.LastName);

                await PublishEventToEventGridAsync(order, correlationInfo.OperationId, correlationInfo);

                Logger.LogInformation("Order {OrderId} processed", order.Id);
            }
        }
Esempio n. 16
0
        /// <summary>
        /// Handle a new <paramref name="message"/> that was received by routing them through registered <see cref="IAzureServiceBusMessageHandler{TMessage}"/>s
        /// and optionally through an registered <see cref="IFallbackMessageHandler"/> or <see cref="IAzureServiceBusFallbackMessageHandler"/> if none of the message handlers were able to process the <paramref name="message"/>.
        /// </summary>
        /// <param name="messageReceiver">
        ///     The instance that can receive Azure Service Bus <see cref="ServiceBusReceivedMessage"/>; used within <see cref="IMessageHandler{TMessage,TMessageContext}"/>s with Azure Service Bus specific operations.
        /// </param>
        /// <param name="message">The message that was received by the <paramref name="messageReceiver"/>.</param>
        /// <param name="messageContext">The context in which the <paramref name="message"/> should be processed.</param>
        /// <param name="correlationInfo">The information concerning correlation of telemetry and processes by using a variety of unique identifiers.</param>
        /// <param name="cancellationToken">The token to cancel the message processing.</param>
        /// <exception cref="ArgumentNullException">
        ///     Thrown when the <paramref name="messageReceiver"/>, <paramref name="message"/>, <paramref name="messageContext"/>, or <paramref name="correlationInfo"/> is <c>null</c>.
        /// </exception>
        /// <exception cref="InvalidOperationException">Thrown when no message handlers or none matching message handlers are found to process the message.</exception>
        protected async Task RouteMessageWithPotentialFallbackAsync(
            ServiceBusReceiver messageReceiver,
            ServiceBusReceivedMessage message,
            AzureServiceBusMessageContext messageContext,
            MessageCorrelationInfo correlationInfo,
            CancellationToken cancellationToken)
        {
            Guard.NotNull(message, nameof(message), "Requires an Azure Service Bus message to be processed by the registered message handlers");
            Guard.NotNull(messageContext, nameof(messageContext), "Requires an Azure Service Bus message context in which the incoming message can be processed");
            Guard.NotNull(correlationInfo, nameof(correlationInfo), "Requires an correlation information to correlate between incoming Azure Service Bus messages");

            try
            {
                MessageHandler[] messageHandlers = GetRegisteredMessageHandlers().ToArray();
                if (messageHandlers.Length <= 0 && !HasFallbackMessageHandler && !HasAzureServiceBusFallbackHandler)
                {
                    throw new InvalidOperationException(
                              $"Azure Service Bus message pump cannot correctly process the message in the '{nameof(AzureServiceBusMessageContext)}' "
                              + "because no 'IAzureServiceBusMessageHandler<>' was registered in the dependency injection container. "
                              + $"Make sure you call the correct 'WithServiceBusMessageHandler' extension on the {nameof(IServiceCollection)} "
                              + "during the registration of the Azure Service Bus message pump or message router to register a message handler");
                }

                Encoding encoding    = messageContext.GetMessageEncodingProperty(Logger);
                string   messageBody = encoding.GetString(message.Body.ToArray());

                foreach (MessageHandler messageHandler in messageHandlers)
                {
                    MessageResult result = await DeserializeMessageForHandlerAsync(messageBody, messageContext, messageHandler);

                    if (result.IsSuccess)
                    {
                        var args = new ProcessMessageEventArgs(message, messageReceiver, cancellationToken);
                        SetServiceBusPropertiesForSpecificOperations(messageHandler, args, messageContext);

                        await messageHandler.ProcessMessageAsync(result.DeserializedMessage, messageContext, correlationInfo, cancellationToken);

                        return;
                    }
                }

                if (!HasFallbackMessageHandler && !HasAzureServiceBusFallbackHandler)
                {
                    throw new InvalidOperationException(
                              $"Message pump cannot correctly process the message in the '{nameof(AzureServiceBusMessageContext)}' "
                              + "because none of the registered 'IAzureServiceBusMessageHandler<,>' implementations in the dependency injection container matches the incoming message type and context; "
                              + $"and no '{nameof(IFallbackMessageHandler)}' or '{nameof(IAzureServiceBusFallbackMessageHandler)}' was registered to fall back to."
                              + $"Make sure you call the correct '.WithServiceBusMessageHandler' extension on the {nameof(IServiceCollection)} during the registration of the message pump or message router to register a message handler");
                }

                await TryFallbackProcessMessageAsync(messageBody, messageContext, correlationInfo, cancellationToken);
                await TryServiceBusFallbackMessageAsync(messageReceiver, message, messageContext, correlationInfo, cancellationToken);
            }
            catch (Exception exception)
            {
                Logger.LogCritical(exception, "Unable to process message with ID '{MessageId}'", message.MessageId);
                throw;
            }
        }
Esempio n. 17
0
 /// <summary>
 /// Process the given <paramref name="message"/> in the current <see cref="IMessageHandler{TMessage,TMessageContext}"/> representation.
 /// </summary>
 /// <param name="message">The parsed message to be processed by the <see cref="IMessageHandler{TMessage,TMessageContext}"/>.</param>
 /// <param name="messageContext">The context providing more information concerning the processing.</param>
 /// <param name="correlationInfo">
 ///     The information concerning correlation of telemetry and processes by using a variety of unique
 ///     identifiers.
 /// </param>
 /// <param name="cancellationToken">The cancellation token.</param>
 public async Task ProcessMessageAsync(
     TMessage message,
     TMessageContext messageContext,
     MessageCorrelationInfo correlationInfo,
     CancellationToken cancellationToken)
 {
     await Service.ProcessMessageAsync(message, messageContext, correlationInfo, cancellationToken);
 }
 /// <summary>
 ///     Process a new message that was received
 /// </summary>
 /// <param name="message">Message that was received</param>
 /// <param name="messageContext">Context providing more information concerning the processing</param>
 /// <param name="correlationInfo">
 ///     Information concerning correlation of telemetry and processes by using a variety of unique
 ///     identifiers
 /// </param>
 /// <param name="cancellationToken">Cancellation token</param>
 public Task ProcessMessageAsync(
     string message,
     MessageContext messageContext,
     MessageCorrelationInfo correlationInfo,
     CancellationToken cancellationToken)
 {
     return(Task.CompletedTask);
 }
 /// <summary>Process a new message that was received</summary>
 /// <param name="message">Message that was received</param>
 /// <param name="messageContext">Context providing more information concerning the processing</param>
 /// <param name="correlationInfo">
 ///     Information concerning correlation of telemetry and processes by using a variety of unique
 ///     identifiers
 /// </param>
 /// <param name="cancellationToken">Cancellation token</param>
 public async Task ProcessMessageAsync(
     Order message,
     AzureServiceBusMessageContext messageContext,
     MessageCorrelationInfo correlationInfo,
     CancellationToken cancellationToken)
 {
     throw new NotImplementedException();
 }
 /// <summary>
 ///     Process a new message that was received
 /// </summary>
 /// <param name="message">Message that was received</param>
 /// <param name="messageContext">Context providing more information concerning the processing</param>
 /// <param name="correlationInfo">
 ///     Information concerning correlation of telemetry and processes by using a variety of unique
 ///     identifiers
 /// </param>
 /// <param name="cancellationToken">Cancellation token</param>
 public Task ProcessMessageAsync(
     ServiceBusReceivedMessage message,
     AzureServiceBusMessageContext messageContext,
     MessageCorrelationInfo correlationInfo,
     CancellationToken cancellationToken)
 {
     return(Task.CompletedTask);
 }
 /// <summary>
 ///     Process a new message that was received
 /// </summary>
 /// <param name="message">Message that was received</param>
 /// <param name="messageContext">Context providing more information concerning the processing</param>
 /// <param name="correlationInfo">
 ///     Information concerning correlation of telemetry and processes by using a variety of unique
 ///     identifiers
 /// </param>
 /// <param name="cancellationToken">Cancellation token</param>
 public Task ProcessMessageAsync(
     TestMessage message,
     MessageContext messageContext,
     MessageCorrelationInfo correlationInfo,
     CancellationToken cancellationToken)
 {
     IsProcessed = true;
     return(Task.CompletedTask);
 }
Esempio n. 22
0
 /// <summary>
 ///     Process a new message that was received
 /// </summary>
 /// <param name="message">Message that was received</param>
 /// <param name="messageContext">Context providing more information concerning the processing</param>
 /// <param name="correlationInfo">
 ///     Information concerning correlation of telemetry and processes by using a variety of unique
 ///     identifiers
 /// </param>
 /// <param name="cancellationToken">Cancellation token</param>
 public Task ProcessMessageAsync(
     Order message,
     AzureServiceBusMessageContext messageContext,
     MessageCorrelationInfo correlationInfo,
     CancellationToken cancellationToken)
 {
     throw new InvalidOperationException(
               "Sabotage the message processing with an unhandled exception");
 }
 /// <summary>
 ///     Process a new message that was received
 /// </summary>
 /// <param name="message">Message that was received</param>
 /// <param name="messageContext">Context providing more information concerning the processing</param>
 /// <param name="correlationInfo">
 ///     Information concerning correlation of telemetry and processes by using a variety of unique
 ///     identifiers
 /// </param>
 /// <param name="cancellationToken">Cancellation token</param>
 public Task ProcessMessageAsync(
     OrderV2 message,
     AzureServiceBusMessageContext messageContext,
     MessageCorrelationInfo correlationInfo,
     CancellationToken cancellationToken)
 {
     IsProcessed = true;
     return(Task.CompletedTask);
 }
        /// <summary>
        ///     Process a new message that was received
        /// </summary>
        /// <param name="message">Message that was received</param>
        /// <param name="messageContext">Context providing more information concerning the processing</param>
        /// <param name="correlationInfo">
        ///     Information concerning correlation of telemetry and processes by using a variety of unique
        ///     identifiers
        /// </param>
        /// <param name="cancellationToken">Cancellation token</param>
        public override async Task ProcessMessageAsync(
            Order message,
            AzureServiceBusMessageContext messageContext,
            MessageCorrelationInfo correlationInfo,
            CancellationToken cancellationToken)
        {
            await _orderMessageHandler.ProcessMessageAsync(message, messageContext, correlationInfo, cancellationToken);

            await CompleteMessageAsync();
        }
        /// <summary>
        ///     Process a new message that was received
        /// </summary>
        /// <param name="message">The Azure Service Bus Message message that was received</param>
        /// <param name="messageContext">The context providing more information concerning the processing</param>
        /// <param name="correlationInfo">
        ///     The information concerning correlation of telemetry and processes by using a variety of unique
        ///     identifiers.
        /// </param>
        /// <param name="cancellationToken">The cancellation token to cancel the processing.</param>
        public override async Task ProcessMessageAsync(
            ServiceBusReceivedMessage message,
            AzureServiceBusMessageContext messageContext,
            MessageCorrelationInfo correlationInfo,
            CancellationToken cancellationToken)
        {
            await _fallbackMessageHandler.ProcessMessageAsync(message, messageContext, correlationInfo, cancellationToken);

            await CompleteAsync(message);
        }
Esempio n. 26
0
        /// <summary>
        ///     Gets the correlation information for a given message.
        /// </summary>
        /// <param name="message">The received message.</param>
        public static MessageCorrelationInfo GetCorrelationInfo(this Message message)
        {
            Guard.NotNull(message, nameof(message));

            string transactionId = GetTransactionId(message);
            string operationId   = DetermineOperationId(message.CorrelationId);

            var messageCorrelationInfo = new MessageCorrelationInfo(operationId, transactionId);

            return(messageCorrelationInfo);
        }
        /// <summary>
        ///     Process a new message that was received
        /// </summary>
        /// <param name="message">Message that was received</param>
        /// <param name="messageContext">Context providing more information concerning the processing</param>
        /// <param name="correlationInfo">
        ///     Information concerning correlation of telemetry and processes by using a variety of unique
        ///     identifiers
        /// </param>
        /// <param name="cancellationToken">Cancellation token</param>
        public override async Task ProcessMessageAsync(
            Order message,
            AzureServiceBusMessageContext messageContext,
            MessageCorrelationInfo correlationInfo,
            CancellationToken cancellationToken)
        {
            Logger.LogTrace("Dead letter message '{OrderId}'...", message.Id);
            await DeadLetterMessageAsync();

            Logger.LogInformation("Message '{OrderId}' is dead lettered", message.Id);
        }
Esempio n. 28
0
 /// <summary>
 ///     Process a new message that was received
 /// </summary>
 /// <param name="batch">Message that was received</param>
 /// <param name="messageContext">Context providing more information concerning the processing</param>
 /// <param name="correlationInfo">
 ///     Information concerning correlation of telemetry and processes by using a variety of unique
 ///     identifiers
 /// </param>
 /// <param name="cancellationToken">Cancellation token</param>
 public async Task ProcessMessageAsync(
     OrderBatch batch,
     AzureServiceBusMessageContext messageContext,
     MessageCorrelationInfo correlationInfo,
     CancellationToken cancellationToken)
 {
     foreach (Order order in batch.Orders)
     {
         await _messageHandler.ProcessMessageAsync(order, messageContext, correlationInfo, cancellationToken);
     }
 }
        /// <summary>Process a new message that was received</summary>
        /// <param name="message">Message that was received</param>
        /// <param name="messageContext">Context providing more information concerning the processing</param>
        /// <param name="correlationInfo">
        ///     Information concerning correlation of telemetry and processes by using a variety of unique
        ///     identifiers
        /// </param>
        /// <param name="cancellationToken">Cancellation token</param>
        public async Task ProcessMessageAsync(
            EmptyMessage message,
            AzureServiceBusMessageContext messageContext,
            MessageCorrelationInfo correlationInfo,
            CancellationToken cancellationToken)
        {
            _logger.LogTrace("Processing message {message}...", message);

            // Process message.

            _logger.LogInformation("Message {message} processed!", message);
        }
        /// <summary>
        ///     Gets the correlation information for a given <paramref name="message"/>.
        /// </summary>
        /// <param name="message">The received message.</param>
        /// <param name="transactionIdPropertyName">Name of the property to determine the transaction id.</param>
        /// <returns>
        ///     The correlation information wrapped inside an <see cref="MessageCorrelationInfo"/>,
        ///     containing the operation and transaction ID from the received <paramref name="message"/>;
        ///     otherwise both will be generated GUID's.
        /// </returns>
        /// <exception cref="ArgumentNullException">Thrown when the <paramref name="message"/>.</exception>
        /// <exception cref="ArgumentException">Thrown when the <paramref name="transactionIdPropertyName"/> is blank.</exception>
        public static MessageCorrelationInfo GetCorrelationInfo(this ServiceBusReceivedMessage message, string transactionIdPropertyName)
        {
            Guard.NotNull(message, nameof(message), "Requires an received Azure Service Bus message to retrieve the correlation information");
            Guard.NotNullOrWhitespace(transactionIdPropertyName, nameof(transactionIdPropertyName), "Requires a non-blank property name to retrieve the correlation transaction ID from the received Azure Service Bus message");

            string transactionId = DetermineTransactionId(message, transactionIdPropertyName);
            string operationId   = DetermineOperationId(message.CorrelationId);

            var messageCorrelationInfo = new MessageCorrelationInfo(operationId, transactionId);

            return(messageCorrelationInfo);
        }