'Azure Service Bus client in dependency injection with Dynamic Connection string
I have an azure function which uses service bus topic. Also I have to write messages into two service bus topics. So obviously two connection strings for the the two topics (I am using topic level connection strings).
Initially I have only one topic and I implemented the dependency injection like this
var serviceBusConnectionString = configuration.GetSection("ServiceBusConnectionString").Value;
if (string.IsNullOrEmpty(serviceBusConnectionString))
{
throw new InvalidOperationException(
"Please specify a valid ServiceBusConnectionString in the Azure Functions Settings or your local.settings.json file.");
}
//using AMQP as transport
services.AddSingleton((s) => {
return new ServiceBusClient(serviceBusConnectionString, new ServiceBusClientOptions() { TransportType = ServiceBusTransportType.AmqpWebSockets });
});
and injected it like this
private readonly ServiceBusClient _serviceBusClient;
public MessageBrokerService(ServiceBusClient serviceBusClient)
{
_serviceBusClient = serviceBusClient;
}
public async Task<Message> PushToTopic(string topic, string message)
{
Message m = new Message();
try
{
var sender = _serviceBusClient.CreateSender(topic);
var msg = new ServiceBusMessage(message);
await sender.SendMessageAsync(msg);
m.Status = Domain.Enums.Status.Success;
m.Remarks = "Message posted successfull";
}
catch (ServiceBusException ex)
{
m.Status = Domain.Enums.Status.Failure;
m.Remarks = ex.Message;
}
catch(Exception ex)
{
m.Status = Domain.Enums.Status.Failure;
m.Remarks = ex.Message;
}
m.Timestamp = "";
return m;
}
But since I have two connection strings depending on the topic the calling servicepassed into the method, how can I achieve this.
That means single client, but switch conenction string based on topic in dependency injection
Solution 1:[1]
That means single client, but switch conenction string based on topic in dependency injection
This isn't possible with a single client; each ServiceBusClient
instance is strongly bound to a single namespace. For each connection string, you'll need an individual client.
To my knowledge, the .NET DI container still does not support named instances; a common pattern for scenarios where you need multiple registrations for a single type is to inject a factory.
In your scenario, it would be beneficial to avoid creating a sender for each invocation. That pattern has a non-trivial amount of overhead that you'd need to pay each time you send a message. Senders and receivers, like the client, are intended to be long-lived and we'd encourage using them as a singleton.
I'd recommend injecting a factory that allows you to request a sender for a given connection string + topic pair. This allows them to be created as needed and then cached for reuse. An example would look something like:
// This class is intended to be treated as a singleton. Each client instance
// created will open an independent connection to a Service Bus namespace, shared
// by senders and receivers spawned from it.
public class ServiceBusFactory : IAsyncDisposable
{
private readonly ConcurrentDictionary<string, ServiceBusClient> _clients = new();
private readonly ConcurrentDictionary<(string, string), ServiceBusSender> _senders = new();
public ServiceBusSender GetSender(string connectionString, string entity)
{
var client = _clients.GetOrAdd(connectionString, new ServiceBusClient(connectionString));
return _senders.GetOrAdd((connectionString, entity), tuple => client.CreateSender(tuple.Item2));
}
public async ValueTask DisposeAsync()
{
await Task.WhenAll(_clients.Select(pair => pair.Value.DisposeAsync().AsTask())).ConfigureAwait(false);
GC.SuppressFinalize(this);
}
}
If you choose to keep creating senders per-request, you'll want to be sure that you also close or dispose them when you're done - unless you're also disposing the client. Otherwise, they'll continue to hold network resources until they've been idle long enough that they're cleaned up.
Solution 2:[2]
For displaying syntax issues in Sublime Text, check out https://github.com/SublimeLinter/SublimeLinter for that functionality.
First install Package Control from within Sublime Test by opening the Command Palette:
Win/Linux: ctrl+shift+p
, Mac: cmd+shift+p
(From inside Sublime Text)
Then type: Install Package Control
in the Command Palette and press Enter.
(Or you can access from the Top Menu with Tools
-> Package Control
.)
Then install SublimeLinter
from within the Package Control of Sublime Text.
Then make sure to change default settings from Package Control:
SublimeLinter: Show Errors on Save
Reference Documentation:
- https://www.sublimelinter.com/en/stable/
- https://packagecontrol.io/installation
- https://packagecontrol.io/packages/SublimeLinter
- https://www.digitalocean.com/community/tutorials/how-to-catch-your-errors-in-sublime-text-3
- https://www.sublimelinter.com/en/v3.10.10/lint_modes.html#the-modes
- https://www.sublimelinter.com/en/v3.10.10/lint_modes.html#showing-errors-on-save
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | |
Solution 2 |