I'm trying to handle a number of subscriptions in and Azure.Messaging.SericeBus.
The Docs suggest that I should register my ServiceBusClient
, ServiceBusSender
and ServiceBusProcessor
for DI.
For the latter, it means I need an instance for each subscription, so I have something like this...
services.AddSingleton(provider =>
{
var options = provider.GetService<IOptions<WorkerOptions>>()
.Value;
var client = provider.GetService<ServiceBusClient>();
var subscription = // code to determine the subscription to use
return client.CreateProcessor(options.Topic, subscription);
}
Now I need to instantiate the processors and that's where I come unstuck. In this example I'm using an IServiceProvider
but I think I'm going to have the same problem just using DI and constructor injection.
var processor = MyServiceProvider.GetService<ServiceBusProcessor>()!;
How do I get a specific ServiceBusProcessor
?
I thought I should be able to "name" each instance but that doesn't appear to be possible.
What am I missing?
CodePudding user response:
With .NET Core DI, you need to use separate types to discern between the injection targets. One way to do this is to create a dedicated class for each subscription, e.g.
public abstract class ProcessorProvider
{
private readonly ServiceBusProcessor _proc;
public ProcessorProvider(ServiceBusProcessor proc)
{
_proc = proc;
}
public virtual ServiceBusProcessor Processor { get => _proc; }
}
public class ProcessorProviderA : ProcessorProvider
{
public ProcessorProviderA(ServiceBusProcessor proc): base(proc) {}
}
public class ProcessorProviderB : ProcessorProvider
{
public ProcessorProviderB(ServiceBusProcessor proc): base(proc) {}
}
In your classes, you do not inject the processor directly, but rely on the different classes that provide the processor, e.g.
public class ClassThatReliesOnSubscriptionA
{
private readonly ServiceBusProcessor _proc;
public ClassThatReliesOnSubscriptionA(ProcessorProviderA procProv)
{
_proc = _procProv.Processor;
}
}
// Same for subscription B
This way, you can add a registration for IProcessorProviderForSubscriptionA and IProcessorProviderForSubscriptionB like this:
services.AddSingleton<ProcessorProviderA>(provider =>
{
var options = provider.GetService<IOptions<WorkerOptions>>()
.Value;
var client = provider.GetService<ServiceBusClient>();
var subscription = // Access subscription A
var proc = client.CreateProcessor(options.Topic, subscription);
return new ProcessorProviderA(proc);
}
services.AddSingleton<ProcessorProviderB>(provider =>
{
var options = provider.GetService<IOptions<WorkerOptions>>()
.Value;
var client = provider.GetService<ServiceBusClient>();
var subscription = // Access subscription B
var proc = client.CreateProcessor(options.Topic, subscription);
return new ProcessorProviderB(proc);
}
This way the inversion of control container can discern between the types that are required by the classes (ClassThatReliesOnSubscriptionA
in this sample). Please note that above code is a sample that can give you an outline on how to solve the problem. You can optimize the code further, e.g. by moving common steps into ProcessorProvider
. In order to improve "mockability" in unit tests, you could also use marker interfaces instead of the classes.