{"id":407830,"date":"2024-06-29T19:47:46","date_gmt":"2024-06-29T19:47:46","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=407830"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=407830","title":{"rendered":"<span>A little life hack when you work with Azure Service Bus and ASP.NET Core<\/span>"},"content":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p>Some of the features of your website require message queue integration. It is not a complex task for most developers. If you work with Azure infrastructure, you are able to choose Azure Service Bus as a queue engine. It sounds quite simple: just create Azure Resource, write some code and then be happy! But what would you say if the resources are limited? What will you do if there are several teammates in your team, and all of you have to debug queues at the same time?<\/p>\n<p>Well, I know a minor life hack for my teams. I create an InMemory Message queue engine for local development and use Azure Service Bus (or any other external MQ engine) only for remote environments. This solution allows me to not think about paid resources or concurrency access to the single development queue.<\/p>\n<p>Developers just create business logic and do not care about Azure Access or availability. I think the InMemory engine should not become an issue. Most of the business tasks do not depend on the technical implementation of the queue engine. My opinion that they should not do it at all. When you have to develop a technical algorithm that uses, for example, some Kafka or RabbitMQ features, you will debug it using external resources. But in my opinion, business logic should not depend on either RabbitMQ or Kafka, or Azure Service Bus. When you write unit-tests, you do the same, don&#8217;t you? Therefore the logic can use the InMemory solution during the local development.<\/p>\n<p>So, let me show my solution. If you meet a similar task, the solution could be helpful for you. As an example, I will use an email distribution service (EDS) that accepts emails via Queues and then sends them. My apps publish email content, my EDS consumes it and sends using the SMTP server.<\/p>\n<p>Therefore, we need to develop the following items:<\/p>\n<ol>\n<li>\n<p>Settings for our application<\/p>\n<\/li>\n<li>\n<p>Queue message publisher<\/p>\n<\/li>\n<li>\n<p>Queue consumer.<\/p>\n<\/li>\n<\/ol>\n<h3>Using InMemory Queues engine<\/h3>\n<h4>InMemory Setup<\/h4>\n<p>I will use the\u00a0<a href=\"https:\/\/masstransit-project.com\/\" rel=\"noopener noreferrer nofollow\">MassTransit<\/a>\u00a0library to make the solution simpler. Here is a code that sets the MassTransit:<\/p>\n<pre><code class=\"cs\">\/\/ IServiceCollection services;  services.AddMassTransit(x => {     x.AddConsumer&lt;MassTransitEmailSendConsumer>();     x.UsingInMemory((context, cfg) =>     {         cfg.TransportConcurrencyLimit = 100;         cfg.ConfigureEndpoints(context);         cfg.ReceiveEndpoint(_configuration.EmailMessageTopic.ToString(), e =>         {             e.ConfigureConsumer&lt;MassTransitEmailSendConsumer>(context);         });     }); });  services.AddMassTransitHostedService(); services.AddScoped&lt;IMessageBroker, InMemoryBrokerPublisher>();<\/code><\/pre>\n<p>Here I use some config values. The class represents MQ settings and is used by both queues: InMemory and Azure Service Bus.<\/p>\n<pre><code class=\"cs\">using Microsoft.Extensions.Configuration;  namespace YourNamespace {     public class MessageBrokerSettings     {         public NonNullableString Connection { get; }          public NonNullableString EmailMessageTopic { get; }          public NonNullableString HealthCheckConnection { get; }          public NonNullableString HealthCheckTopic { get; }          public MessageBrokerSettings(IConfiguration configuration)         {             var section = configuration.GetSection(\"Azure\").GetSection(\"ServiceBus\");             Connection = new NonNullableString(section[nameof(Connection)]);             EmailMessageTopic = new NonNullableString(section[nameof(EmailMessageTopic)]);             HealthCheckConnection = new NonNullableString(section[nameof(HealthCheckConnection)]);             HealthCheckTopic = new NonNullableString(section[nameof(HealthCheckTopic)]);         }     } }<\/code><\/pre>\n<p><code>NonNullableString<\/code>\u00a0is a special class that makes me sure that the value inside will never be null. Some kind of ValueObject from DDD, you know. When I invoke<code>the<\/code>method\u00a0<code>.ToString()<\/code>, the class returns me a value of the config. Otherwise, it will throw an exception. The code of the class you may see at my GitHub gist:\u00a0<a href=\"https:\/\/gist.github.com\/maximgorbatyuk\/b772cb40d5bea823be8dc828e7e4ac27#file-nonnullablestring-cs\" rel=\"noopener noreferrer nofollow\">NonNullableString.cs<\/a>.<\/p>\n<h4>InMemory Publisher<\/h4>\n<p>Now we have created a publisher and consumer. The email publisher will use\u00a0<code>IPublishEnpoint<\/code>\u00a0that is given us by MassTransit library:<\/p>\n<pre><code class=\"cs\">using System.Threading.Tasks; using MassTransit; using Microsoft.Extensions.Logging;  namespace YourNamespace {     public class InMemoryBrokerPublisher : BrokerPublisherBase     {         private readonly IPublishEndpoint _publish;          public InMemoryBrokerPublisher(IPublishEndpoint publish, ILogger&lt;InMemoryBrokerPublisher> logger)             : base(logger)         {             _publish = publish;         }          protected override Task PublishInternalAsync&lt;T>(string topicName, T message)         {             return _publish.Publish(message);         }     } }<\/code><\/pre>\n<p>The\u00a0<a href=\"https:\/\/gist.github.com\/maximgorbatyuk\/b772cb40d5bea823be8dc828e7e4ac27#file-brokerpublisherbase-cs\" rel=\"noopener noreferrer nofollow\">BrokerPublisherBase<\/a>\u00a0is a base class and does not depend on queue implementation. The class is inherited by both queue-related publishers as well. It implements a simple IMessageBroker.<\/p>\n<pre><code class=\"cs\">using System.Threading.Tasks;  namespace YourNamespace {     public interface IMessageBroker     {         Task PublishAsync&lt;T>(string topicName, T message)             where T : class;     } }<\/code><\/pre>\n<p>This interface gives the other business logic an endpoint to publish any message.<\/p>\n<h4>InMemory Consumer<\/h4>\n<p>We will use MassTransit\u2019s ConsumerBase interface for InMemory consumers. Here is a content of the\u00a0<code>MassTransitEmailSendConsumer<\/code>:<\/p>\n<pre><code class=\"cs\">using System.Threading.Tasks; using MassTransit; using Microsoft.Extensions.Logging;  namespace YourNamespace {     public class MassTransitEmailSendConsumer : ConsumerBase&lt;EmailMessage>     {         private readonly IEmail _email;          protected override async Task ConsumeAsync(ConsumeContext&lt;EmailMessage> context)         {             await _email.SendAsync(context.Message);             Logger.LogDebug(\u201cEmail sent\u201d);         }          public MassTransitEmailSendConsumer(ILogger&lt;MassTransitEmailSendConsumer> logger, IEmail email)             : base(logger)         {             _email = email;         }     } }<\/code><\/pre>\n<p><code>IEmail<\/code>\u00a0is my business logic interface who is responsible for sending emails. The content of the class does not related to the article subject, and that\u2019s why I don\u2019t give a content of the class. The\u00a0<code>MassTransitEmailSendConsumer<\/code>\u00a0inherits from my own\u00a0<a href=\"https:\/\/gist.github.com\/maximgorbatyuk\/b772cb40d5bea823be8dc828e7e4ac27#file-consumerbase-cs\" rel=\"noopener noreferrer nofollow\">ConsumerBase.cs<\/a>\u00a0class implementing MassTransit\u2019s\u00a0<code>IConsumer<\/code>.<\/p>\n<p>Now our ASP.NET core app could work with Message Queues using only memory. Let\u2019s continue with Azure services.<\/p>\n<h3>Using Azure Service Bus queues<\/h3>\n<p>I will not tell you about how to create an Azure Service Bus (ASB) using portal.azure.com. Here is a\u00a0<a href=\"https:\/\/docs.microsoft.com\/en-us\/azure\/service-bus-messaging\/service-bus-create-namespace-portal\" rel=\"noopener noreferrer nofollow\">tutorial<\/a>\u00a0made by Microsoft. Let\u2019s assume that we have already got a connection string of the Service Bus. How to get it, please read the tutorial from the MS above.<\/p>\n<p>I have created one queue for emailing and a special topic for the Azure health check. If you don\u2019t need the health-check, you may create only needed queues.<\/p>\n<h4>Azure SB Setup<\/h4>\n<p>First, we should set up our application to work with the ASB.<\/p>\n<pre><code class=\"cs\">\/\/ IServiceCollection services; \/\/ MessageBrokerSettings configuration;  services.AddHostedService&lt;AzureBrokerEmailConsumerBackService>(); services.AddScoped&lt;IMessageBroker, AzureServiceBusPublisher>();  services .AddHealthChecks() .AddAzureServiceBusTopic( connectionString: configuration.HealthCheckConnection.ToString(), topicName: configuration.HealthCheckTopic.ToString());<\/code><\/pre>\n<p>My app\u2019s\u00a0<code>appsettings.json<\/code>\u00a0file contains the following values:<\/p>\n<pre><code class=\"json\">\u201cMessageBroker\u201d: {   \u201cConnection\u201d: \u201cEndpoint=sb:\/\/yournamespace.windows.net\/;SharedAccessKeyName=email;SharedAccessKey=awesomesecret\u201d,   \u201cEmailMessageTopic\u201d: \u201cemail-message-queue\u201d,   \u201cHealthCheckConnection\u201d: \u201cEndpoint=sb:\/\/yournamespace.windows.net\/;SharedAccessKeyName=healthcheck;SharedAccessKey=awesomesecret\u201d,   \u201cHealthCheckTopic\u201d: \u201cazuretopic\u201d }, \u201cUseInMemoryMessageBroker\u201d: true,<\/code><\/pre>\n<p>The <code>MessageBroker<\/code>section is being used by\u00a0<code>MessageBrokerSettings<\/code>\u00a0class.\u00a0<code>azuretopic<\/code>\u00a0value is a service name of the topic and is used by the Health-check library.<\/p>\n<h4>Azure SB Publisher<\/h4>\n<p>The ASB accepts a string as the queue message, therefore we have to serialize a message. I use the JSON format for the serialization. Here is a code of my publisher:<\/p>\n<pre><code class=\"cs\">using System.Threading.Tasks; using Azure.Messaging.ServiceBus; using Microsoft.Extensions.Logging; using Newtonsoft.Json;  namespace YourNamespace {     public class AzureServiceBusPublisher : BrokerPublisherBase     {         private readonly MessageBrokerSettings _config;          public AzureServiceBusPublisher(MessageBrokerSettings configuration, ILogger&lt;AzureServiceBusPublisher> logger)             : base(logger)         {             _config = configuration;         }          protected override async Task PublishInternalAsync&lt;T>(string topicName, T message)         {             \/\/ create a Service Bus client             await using var client = new ServiceBusClient(_config.Connection.ToString());              ServiceBusSender sender = client.CreateSender(topicName);              \/\/ create a message that we can send             \/\/ send the message             await sender.SendMessageAsync(                 new ServiceBusMessage(JsonConvert.SerializeObject(message)));         }     } }<\/code><\/pre>\n<p>Please pay attention that the class above uses\u00a0<a href=\"https:\/\/gist.github.com\/maximgorbatyuk\/b772cb40d5bea823be8dc828e7e4ac27#file-brokerpublisherbase-cs\" rel=\"noopener noreferrer nofollow\">BrokerPublisherBase<\/a>\u00a0as a parent. We create\u00a0<code>ServiceBusClient<\/code>\u00a0for each invocation of the class, and this way is\u00a0<a href=\"https:\/\/docs.microsoft.com\/en-us\/azure\/service-bus-messaging\/service-bus-dotnet-get-started-with-queues#add-code-to-send-messages-to-the-queue\" rel=\"noopener noreferrer nofollow\">recommended<\/a>\u00a0by Microsoft.<\/p>\n<h4>Azure SB Consumer<\/h4>\n<p>Consuming the SB queue message is not as simple as publishing. We should create a hosted service to consume messages within the background process of the ASP.NET Core app. We will use a\u00a0<code>BackgroundService<\/code>\u00a0provided by .net library. We will setup Callbacks for messages and possible errors, and then we will start an endless loop to make the background service working during the main app execution.<\/p>\n<pre><code class=\"cs\">using System; using System.Threading.Tasks; using Azure.Messaging.ServiceBus; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging;  namespace YourNamespace {     public class AzureBrokerEmailConsumerBackService : AzureBusTopicConsumerBase     {         public AzureBrokerEmailConsumerBackService(             ILogger&lt;AzureBrokerEmailConsumerBackService> logger,             IServiceScopeFactory scopeFactory,             MessageBrokerSettings brokerSettings)             : base(                 logger,                 scopeFactory,                 brokerSettings)         {         }          \/\/ handle received messages         protected override NonNullableString MessageTopic => BrokerSettings.EmailMessageTopic;          protected override Task MessageHandleInternalAsync(IServiceProvider provider, ServiceBusReceivedMessage message)         {             string body = message.Body.ToString();             var email = provider.GetRequiredService&lt;IEmail>();             return email.SendAsync(body);         }     } }<\/code><\/pre>\n<p>The consumer above inherits from our special class\u00a0<a href=\"https:\/\/gist.github.com\/maximgorbatyuk\/b772cb40d5bea823be8dc828e7e4ac27#file-azurebustopicconsumerbase-cs\" rel=\"noopener noreferrer nofollow\">AzureBusTopicConsumerBase<\/a>. This class hides most of the code that sets up the background service. Also, the class creates scope for each received message and then provides an instance of\u00a0<code>IServiceProvider provider<\/code>. The provider is useful to get any business service to execute your task:<\/p>\n<pre><code class=\"cs\">using var scope = ScopeFactory.CreateScope(); await MessageHandleInternalAsync(scope.ServiceProvider, args.Message);  \/\/ complete the message. messages is deleted from the queue. await args.CompleteMessageAsync(args.Message);<\/code><\/pre>\n<h3>Conclusion<\/h3>\n<p>All you need is a config class that will decide what MQ engine will be used for the running application: the InMemory MQ engine either Azure Service Bus. I have created\u00a0<a href=\"https:\/\/gist.github.com\/maximgorbatyuk\/b772cb40d5bea823be8dc828e7e4ac27#file-messagebrokerconfig-cs\" rel=\"noopener noreferrer nofollow\">a helper-class<\/a>\u00a0for this purpose, so you can use it as well. Now you have an application that uses Azure Service Bus for staging and production environments and InMemory engine for the local development.<\/p>\n<p>Hope my article was useful for you. Thank you for the reading!<\/p>\n<\/div>\n<\/div>\n<\/div>\n<p><!----><!----><\/div>\n<p><!----><!----><br \/> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/articles\/546138\/\"> https:\/\/habr.com\/ru\/articles\/546138\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p>Some of the features of your website require message queue integration. It is not a complex task for most developers. If you work with Azure infrastructure, you are able to choose Azure Service Bus as a queue engine. It sounds quite simple: just create Azure Resource, write some code and then be happy! But what would you say if the resources are limited? What will you do if there are several teammates in your team, and all of you have to debug queues at the same time?<\/p>\n<p>Well, I know a minor life hack for my teams. I create an InMemory Message queue engine for local development and use Azure Service Bus (or any other external MQ engine) only for remote environments. This solution allows me to not think about paid resources or concurrency access to the single development queue.<\/p>\n<p>Developers just create business logic and do not care about Azure Access or availability. I think the InMemory engine should not become an issue. Most of the business tasks do not depend on the technical implementation of the queue engine. My opinion that they should not do it at all. When you have to develop a technical algorithm that uses, for example, some Kafka or RabbitMQ features, you will debug it using external resources. But in my opinion, business logic should not depend on either RabbitMQ or Kafka, or Azure Service Bus. When you write unit-tests, you do the same, don&#8217;t you? Therefore the logic can use the InMemory solution during the local development.<\/p>\n<p>So, let me show my solution. If you meet a similar task, the solution could be helpful for you. As an example, I will use an email distribution service (EDS) that accepts emails via Queues and then sends them. My apps publish email content, my EDS consumes it and sends using the SMTP server.<\/p>\n<p>Therefore, we need to develop the following items:<\/p>\n<ol>\n<li>\n<p>Settings for our application<\/p>\n<\/li>\n<li>\n<p>Queue message publisher<\/p>\n<\/li>\n<li>\n<p>Queue consumer.<\/p>\n<\/li>\n<\/ol>\n<h3>Using InMemory Queues engine<\/h3>\n<h4>InMemory Setup<\/h4>\n<p>I will use the\u00a0<a href=\"https:\/\/masstransit-project.com\/\" rel=\"noopener noreferrer nofollow\">MassTransit<\/a>\u00a0library to make the solution simpler. Here is a code that sets the MassTransit:<\/p>\n<pre><code class=\"cs\">\/\/ IServiceCollection services;  services.AddMassTransit(x => {     x.AddConsumer&lt;MassTransitEmailSendConsumer>();     x.UsingInMemory((context, cfg) =>     {         cfg.TransportConcurrencyLimit = 100;         cfg.ConfigureEndpoints(context);         cfg.ReceiveEndpoint(_configuration.EmailMessageTopic.ToString(), e =>         {             e.ConfigureConsumer&lt;MassTransitEmailSendConsumer>(context);         });     }); });  services.AddMassTransitHostedService(); services.AddScoped&lt;IMessageBroker, InMemoryBrokerPublisher>();<\/code><\/pre>\n<p>Here I use some config values. The class represents MQ settings and is used by both queues: InMemory and Azure Service Bus.<\/p>\n<pre><code class=\"cs\">using Microsoft.Extensions.Configuration;  namespace YourNamespace {     public class MessageBrokerSettings     {         public NonNullableString Connection { get; }          public NonNullableString EmailMessageTopic { get; }          public NonNullableString HealthCheckConnection { get; }          public NonNullableString HealthCheckTopic { get; }          public MessageBrokerSettings(IConfiguration configuration)         {             var section = configuration.GetSection(\"Azure\").GetSection(\"ServiceBus\");             Connection = new NonNullableString(section[nameof(Connection)]);             EmailMessageTopic = new NonNullableString(section[nameof(EmailMessageTopic)]);             HealthCheckConnection = new NonNullableString(section[nameof(HealthCheckConnection)]);             HealthCheckTopic = new NonNullableString(section[nameof(HealthCheckTopic)]);         }     } }<\/code><\/pre>\n<p><code>NonNullableString<\/code>\u00a0is a special class that makes me sure that the value inside will never be null. Some kind of ValueObject from DDD, you know. When I invoke<code>the<\/code>method\u00a0<code>.ToString()<\/code>, the class returns me a value of the config. Otherwise, it will throw an exception. The code of the class you may see at my GitHub gist:\u00a0<a href=\"https:\/\/gist.github.com\/maximgorbatyuk\/b772cb40d5bea823be8dc828e7e4ac27#file-nonnullablestring-cs\" rel=\"noopener noreferrer nofollow\">NonNullableString.cs<\/a>.<\/p>\n<h4>InMemory Publisher<\/h4>\n<p>Now we have created a publisher and consumer. The email publisher will use\u00a0<code>IPublishEnpoint<\/code>\u00a0that is given us by MassTransit library:<\/p>\n<pre><code class=\"cs\">using System.Threading.Tasks; using MassTransit; using Microsoft.Extensions.Logging;  namespace YourNamespace {     public class InMemoryBrokerPublisher : BrokerPublisherBase     {         private readonly IPublishEndpoint _publish;          public InMemoryBrokerPublisher(IPublishEndpoint publish, ILogger&lt;InMemoryBrokerPublisher> logger)             : base(logger)         {             _publish = publish;         }          protected override Task PublishInternalAsync&lt;T>(string topicName, T message)         {             return _publish.Publish(message);         }     } }<\/code><\/pre>\n<p>The\u00a0<a href=\"https:\/\/gist.github.com\/maximgorbatyuk\/b772cb40d5bea823be8dc828e7e4ac27#file-brokerpublisherbase-cs\" rel=\"noopener noreferrer nofollow\">BrokerPublisherBase<\/a>\u00a0is a base class and does not depend on queue implementation. The class is inherited by both queue-related publishers as well. It implements a simple IMessageBroker.<\/p>\n<pre><code class=\"cs\">using System.Threading.Tasks;  namespace YourNamespace {     public interface IMessageBroker     {         Task PublishAsync&lt;T>(string topicName, T message)             where T : class;     } }<\/code><\/pre>\n<p>This interface gives the other business logic an endpoint to publish any message.<\/p>\n<h4>InMemory Consumer<\/h4>\n<p>We will use MassTransit\u2019s ConsumerBase interface for InMemory consumers. Here is a content of the\u00a0<code>MassTransitEmailSendConsumer<\/code>:<\/p>\n<pre><code class=\"cs\">using System.Threading.Tasks; using MassTransit; using Microsoft.Extensions.Logging;  namespace YourNamespace {     public class MassTransitEmailSendConsumer : ConsumerBase&lt;EmailMessage>     {         private readonly IEmail _email;          protected override async Task ConsumeAsync(ConsumeContext&lt;EmailMessage> context)         {             await _email.SendAsync(context.Message);             Logger.LogDebug(\u201cEmail sent\u201d);         }          public MassTransitEmailSendConsumer(ILogger&lt;MassTransitEmailSendConsumer> logger, IEmail email)             : base(logger)         {             _email = email;         }     } }<\/code><\/pre>\n<p><code>IEmail<\/code>\u00a0is my business logic interface who is responsible for sending emails. The content of the class does not related to the article subject, and that\u2019s why I don\u2019t give a content of the class. The\u00a0<code>MassTransitEmailSendConsumer<\/code>\u00a0inherits from my own\u00a0<a href=\"https:\/\/gist.github.com\/maximgorbatyuk\/b772cb40d5bea823be8dc828e7e4ac27#file-consumerbase-cs\" rel=\"noopener noreferrer nofollow\">ConsumerBase.cs<\/a>\u00a0class implementing MassTransit\u2019s\u00a0<code>IConsumer<\/code>.<\/p>\n<p>Now our ASP.NET core app could work with Message Queues using only memory. Let\u2019s continue with Azure services.<\/p>\n<h3>Using Azure Service Bus queues<\/h3>\n<p>I will not tell you about how to create an Azure Service Bus (ASB) using portal.azure.com. Here is a\u00a0<a href=\"https:\/\/docs.microsoft.com\/en-us\/azure\/service-bus-messaging\/service-bus-create-namespace-portal\" rel=\"noopener noreferrer nofollow\">tutorial<\/a>\u00a0made by Microsoft. Let\u2019s assume that we have already got a connection string of the Service Bus. How to get it, please read the tutorial from the MS above.<\/p>\n<p>I have created one queue for emailing and a special topic for the Azure health check. If you don\u2019t need the health-check, you may create only needed queues.<\/p>\n<h4>Azure SB Setup<\/h4>\n<p>First, we should set up our application to work with the ASB.<\/p>\n<pre><code class=\"cs\">\/\/ IServiceCollection services; \/\/ MessageBrokerSettings configuration;  services.AddHostedService&lt;AzureBrokerEmailConsumerBackService>(); services.AddScoped&lt;IMessageBroker, AzureServiceBusPublisher>();  services .AddHealthChecks() .AddAzureServiceBusTopic( connectionString: configuration.HealthCheckConnection.ToString(), topicName: configuration.HealthCheckTopic.ToString());<\/code><\/pre>\n<p>My app\u2019s\u00a0<code>appsettings.json<\/code>\u00a0file contains the following values:<\/p>\n<pre><code class=\"json\">\u201cMessageBroker\u201d: {   \u201cConnection\u201d: \u201cEndpoint=sb:\/\/yournamespace.windows.net\/;SharedAccessKeyName=email;SharedAccessKey=awesomesecret\u201d,   \u201cEmailMessageTopic\u201d: \u201cemail-message-queue\u201d,   \u201cHealthCheckConnection\u201d: \u201cEndpoint=sb:\/\/yournamespace.windows.net\/;SharedAccessKeyName=healthcheck;SharedAccessKey=awesomesecret\u201d,   \u201cHealthCheckTopic\u201d: \u201cazuretopic\u201d }, \u201cUseInMemoryMessageBroker\u201d: true,<\/code><\/pre>\n<p>The <code>MessageBroker<\/code>section is being used by\u00a0<code>MessageBrokerSettings<\/code>\u00a0class.\u00a0<code>azuretopic<\/code>\u00a0value is a service name of the topic and is used by the Health-check library.<\/p>\n<h4>Azure SB Publisher<\/h4>\n<p>The ASB accepts a string as the queue message, therefore we have to serialize a message. I use the JSON format for the serialization. Here is a code of my publisher:<\/p>\n<pre><code class=\"cs\">using System.Threading.Tasks; using Azure.Messaging.ServiceBus; using Microsoft.Extensions.Logging; using Newtonsoft.Json;  namespace YourNamespace {     public class AzureServiceBusPublisher : BrokerPublisherBase     {         private readonly MessageBrokerSettings _config;          public AzureServiceBusPublisher(MessageBrokerSettings configuration, ILogger&lt;AzureServiceBusPublisher> logger)             : base(logger)         {             _config = configuration;         }          protected override async Task PublishInternalAsync&lt;T>(string topicName, T message)         {             \/\/ create a Service Bus client             await using var client = new ServiceBusClient(_config.Connection.ToString());              ServiceBusSender sender = client.CreateSender(topicName);              \/\/ create a message that we can send             \/\/ send the message             await sender.SendMessageAsync(                 new ServiceBusMessage(JsonConvert.SerializeObject(message)));         }     } }<\/code><\/pre>\n<p>Please pay attention that the class above uses\u00a0<a href=\"https:\/\/gist.github.com\/maximgorbatyuk\/b772cb40d5bea823be8dc828e7e4ac27#file-brokerpublisherbase-cs\" rel=\"noopener noreferrer nofollow\">BrokerPublisherBase<\/a>\u00a0as a parent. We create\u00a0<code>ServiceBusClient<\/code>\u00a0for each invocation of the class, and this way is\u00a0<a href=\"https:\/\/docs.microsoft.com\/en-us\/azure\/service-bus-messaging\/service-bus-dotnet-get-started-with-queues#add-code-to-send-messages-to-the-queue\" rel=\"noopener noreferrer nofollow\">recommended<\/a>\u00a0by Microsoft.<\/p>\n<h4>Azure SB Consumer<\/h4>\n<p>Consuming the SB queue message is not as simple as publishing. We should create a hosted service to consume messages within the background process of the ASP.NET Core app. We will use a\u00a0<code>BackgroundService<\/code>\u00a0provided by .net library. We will setup Callbacks for messages and possible errors, and then we will start an endless loop to make the background service working during the main app execution.<\/p>\n<pre><code class=\"cs\">using System; using System.Threading.Tasks; using Azure.Messaging.ServiceBus; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging;  namespace YourNamespace {     public class AzureBrokerEmailConsumerBackService : AzureBusTopicConsumerBase     {         public AzureBrokerEmailConsumerBackService(             ILogger&lt;AzureBrokerEmailConsumerBackService> logger,<\/code><\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-407830","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/407830","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=407830"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/407830\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=407830"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=407830"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=407830"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}