We have a service based application in .net core which would run as a daemon in Linux environment. Everything is working as expected but i am having problem in handling dependency injection. Below is the code for reference
Program.cs
class Program
{
static async Task Main(string[] args)
{
Console.WriteLine("Starting PreProcessor Application ");
try
{
ConfigParameters.LoadSettings(args);
}
catch (Exception ex)
{
Console.BackgroundColor = ConsoleColor.Red;
Console.WriteLine($"Error in setting config parameters {ex.Message}");
return;
}
IHost host = new HostBuilder()
.ConfigureServices((hostContext, services) =>
{
services.AddLogging();
services.AddHostedService<MainService>();
services.AddTransient<IMessageQueue, ActiveMQHandler>(x =>
{
return new ActiveMQHandler(ConfigParameters.Settings.MessageQueueAddress);
});
services.AddTransient<IMessageQueue, ActiveMQHandler>(x =>
{
return new ActiveMQHandler(ConfigParameters.Settings.MessageQueueAddress);
});
services.AddTransient<IMessageQueue, ActiveMQHandler>(x =>
{
return new ActiveMQHandler(ConfigParameters.Settings.MessageQueueAddress);
});
})
.Build();
await host.RunAsync();
}
}
Constructor for MainService
looks like this
IApplicationLifetime appLifetime;
IConfiguration configuration;
PreProcessorQueueListener listener;
private string reason = "SHUTDOWN SIGNAL";
private IMessageQueue messageQueue;
private IMessageQueue messageQueueSL;
private IMessageQueue messageQueueSLProcess;
public MainService(IConfiguration configuration, IApplicationLifetime appLifetime, IMessageQueue messageQueue, IMessageQueue messageQueueSL, IMessageQueue messageQueueSLProcess)
{
this.configuration = configuration;
this.messageQueue = messageQueue;
this.messageQueueSL = messageQueueSL;
this.messageQueueSLProcess = messageQueueSLProcess;
this.appLifetime = appLifetime;
}
If you see in my MainService
code i am passing three instances for IMessageQueue
interface using constructor dependency injection. What i really want is based on a need in any part of the application i could grab a new instance of ActiveMQHandler
class by passing IMessageQueue
interface. Since i could not find a solution for this i am passing three instances (i am not happy with this solution) of IMessageQueue
. If i need to use another instance of ActiveMQHandler
class then i will have to pass fourth parameter as IMessageQueue
interface in my MainService
class.
What i am really looking for is use ServiceProvider
(or something more elegant) and use that to get a new / singleton (based on how it is defined in Program.cs
) instance of the class which implements the IMessageQueue
interface.
An suggestions guys??
You can inject the
IServiceProvider
into your class then use theGetServices(typeof(IMessageQueue))
or the extension functionGetServices<IMessageQueue>()
that can be found within theMicrosoft.Extensions.DependencyInejction
namespace. So something like this:There could be more elegant solutions based on what exactly you are using
IMessageQueue
for. It seems likeIMessageQueue
is for some kind of logging. For instance, let's say you needed a message queue for each class whereSLProcess
andSL
were different classes. For such scenarios, you can inject a generic. So you can have something like this defined:With this you should be able to inject something like this:
AddTransient(typeof(IMessageQueue<>), typeof(ActiveMQHandler<>))
.If you change your MainService constructor signature to
you will be able to access all three interface implementations.
The problem might be if you need to identify them from the list, for example to perform different action on each implementation. If you need to perform the same action on each implementation than this will work.
Otherwise, you should consider using generic type to differentiate implementations injected.
Finally i have come with a solution which i think is elegant and is not dependent on constructor DI. Idea is let the service (yes we have a microservice architecture) create the collection of dependencies in
IServiceCollection
and once the service is started any class whenever they want to resolve a Dependency they will just pass in theInterface
and will get the instance of the concrete class. My final code is like this. I have created a separate class in a common libraryNow my
Main
method inProgram.cs
file looks like thisNow anywhere in the project when i need the instance of the ActiveMQHandler class i just write the below line of code
Just for the information in my
Program.cs
i am usingAddScoped
but i have tested the code withAddSingleton
also and everytime i requested for the concrete class instance it was the same.The article at this link https://stackify.com/net-core-dependency-injection/ helped me
Just change the constructor to contain
IEnumerable<IMessageQueue>
. It should give you a list of all registered IMessageQueue implementers.Personally I do not like taking on dependencies on IApplicationLifetime or IServiceProvider in my classes. This is a bit like the ServiceLocator anti-pattern.