简体   繁体   中英

Using Dependency Injection in .NET Core Service application

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??

If you change your MainService constructor signature to

public MainService(IConfiguration configuration, IApplicationLifetime appLifetime, IEnumerable<IMessageQueue> messageQueues)

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.

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.

You can inject the IServiceProvider into your class then use the GetServices(typeof(IMessageQueue)) or the extension function GetServices<IMessageQueue>() that can be found within the Microsoft.Extensions.DependencyInejction namespace. So something like this:

public MainService(IConfiguration configuration, IApplicationLifetime appLifetime, IServiceProvider serviceProvider)
{
    this.configuration = configuration;            
    messageQueue = serviceProvider.GetServices<IMessageQueue>();
    messageQueueSL = serviceProvider.GetServices<IMessageQueue>();
    messageQueueSLProcess = serviceProvider.GetServices<IMessageQueue>();
    this.appLifetime = appLifetime;
}

There could be more elegant solutions based on what exactly you are using IMessageQueue for. It seems like IMessageQueue is for some kind of logging. For instance, let's say you needed a message queue for each class where SLProcess and SL were different classes. For such scenarios, you can inject a generic. So you can have something like this defined:

interface IMessageQueue<T> : IMessageQueue { }

class ActiveMQHandler<T> : ActiveMQHandler, IMessageQueue<T> {

    public string targetType => typeof(T).ToString();
}

With this you should be able to inject something like this: AddTransient(typeof(IMessageQueue<>), typeof(ActiveMQHandler<>)) .

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 the Interface and will get the instance of the concrete class. My final code is like this. I have created a separate class in a common library

public class DependencyInjection
{
    private static ServiceProvider Provider;
    public static void AddServices(IServiceCollection services)
    {
        Provider = services.BuildServiceProvider();
    }

    public static T GetService<T>()
    {
        var serviceScopeFactory = Provider.GetRequiredService<IServiceScopeFactory>();
        using (var scope = serviceScopeFactory.CreateScope())
        {
            return scope.ServiceProvider.GetService<T>();
        }
    }
}

Now my Main method in Program.cs file looks like this

static async Task Main(string[] args)
    {
        Console.WriteLine("Starting PreProcessor Application ");
        IServiceCollection servicesCollection = new ServiceCollection();

        try
        {
            ConfigParameters.LoadSettings(args);
            servicesCollection.AddScoped<IMessageQueue, ActiveMQHandler>(x =>
            {
                return new ActiveMQHandler("127.0.0.1");
            });
            DependencyInjection.AddServices(servicesCollection);
        }
        catch (Exception ex)
        {

            Console.BackgroundColor = ConsoleColor.Red;
            Console.WriteLine($"Error in setting config parameters {ex.Message}");
            return;
        }

        IHost host = new HostBuilder()
            .ConfigureHostConfiguration(configHost =>
            {
                configHost.AddCommandLine(args);
            })
            .ConfigureServices((hostContext, services) =>
            {
                services.AddLogging();
                services.AddHostedService<MainService>();                    
            })
            .Build();            

        await host.RunAsync();
    }

Now anywhere in the project when i need the instance of the ActiveMQHandler class i just write the below line of code

var messageQueue = DependencyInjection.GetService<IMessageQueue>();

Just for the information in my Program.cs i am using AddScoped but i have tested the code with AddSingleton 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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM