0

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

1 Answer 1

2

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.

Sign up to request clarification or add additional context in comments.

2 Comments

You can also inject multiple named instances of ServiceBusClient. See the answer to this question - stackoverflow.com/questions/72110911/…
Nice... I hadn't realized that we injected a factory for named instances!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.