'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:

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