【问题标题】:Using Dependency Injection in .NET Core Service application在 .NET Core 服务应用程序中使用依赖注入
【发布时间】:2019-05-07 14:25:08
【问题描述】:

我们在 .net 核心中有一个基于服务的应用程序,它将在 Linux 环境中作为守护程序运行。一切都按预期工作,但我在处理依赖注入时遇到问题。 下面是代码供参考

程序.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();
    }
}

MainService 的构造函数如下所示

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;
    }

如果您在我的MainService 代码中看到我正在使用构造函数依赖注入为IMessageQueue 接口传递三个实例。我真正想要的是基于应用程序任何部分的需要,我可以通过传递IMessageQueue 接口来获取ActiveMQHandler 类的新实例。由于我找不到解决方案,我正在传递IMessageQueue 的三个实例(我对这个解决方案不满意)。如果我需要使用ActiveMQHandler 类的另一个实例,那么我必须在MainService 类中将第四个参数作为IMessageQueue 接口传递。

我真正想要的是使用ServiceProvider(或更优雅的东西)并使用它来获得一个新的/单例(基于它在Program.cs中的定义)实现@987654334的类的实例@接口。

一个建议??

【问题讨论】:

    标签: c# .net dependency-injection .net-core console


    【解决方案1】:

    最后我提出了一个我认为优雅且不依赖于构造函数 DI 的解决方案。 想法是让服务(是的,我们有一个微服务架构)在IServiceCollection 中创建依赖项的集合,一旦服务启动,任何类只要他们想要解决依赖项,他们就会传入Interface 并获得具体类的实例。 我的最终代码是这样的。我在公共库中创建了一个单独的类

    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>();
            }
        }
    }
    

    现在我在Program.cs 文件中的Main 方法看起来像这样

    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();
        }
    

    现在在项目中的任何地方,当我需要 ActiveMQHandler 类的实例时,我只需编写以下代码行

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

    只是为了我的Program.cs 中的信息,我正在使用AddScoped,但我也用AddSingleton 测试了代码,每次我请求具体的类实例时都是一样的。

    此链接https://stackify.com/net-core-dependency-injection/的文章帮助了我

    【讨论】:

    • 这是 ServiceLocator 模式。我强烈建议您不要这样做,并重新考虑使用构造函数注入来表达您的依赖关系。这样,您不会让框架为您提供依赖项,即反转的全部内容,而是从 DI 容器中请求它,这与新建依赖项几乎相同。
    【解决方案2】:

    只需将构造函数更改为包含IEnumerable&lt;IMessageQueue&gt;。 它应该为您提供所有已注册的 IMessageQueue 实施者的列表。

    我个人不喜欢在我的课程中依赖 IApplicationLifetime 或 IServiceProvider。这有点像 ServiceLocator 反模式。

    【讨论】:

      【解决方案3】:

      如果您将 MainService 构造函数签名更改为

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

      您将能够访问所有三个接口实现。

      问题可能在于您是否需要从列表中识别它们,例如对每个实现执行不同的操作。如果您需要对每个实现执行相同的操作,那么这将起作用。

      否则,您应该考虑使用泛型类型来区分注入的实现。

      【讨论】:

      【解决方案4】:

      您可以将IServiceProvider 注入到您的类中,然后使用GetServices(typeof(IMessageQueue)) 或可以在Microsoft.Extensions.DependencyInejction 命名空间中找到的扩展函数GetServices&lt;IMessageQueue&gt;()。所以是这样的:

      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;
      }
      

      根据您使用IMessageQueue 的确切用途,可能会有更优雅的解决方案。似乎IMessageQueue 用于某种日志记录。例如,假设您需要为每个类创建一个消息队列,其中SLProcess 和SL 是不同的类。对于这种情况,您可以注入泛型。所以你可以有这样的定义:

      interface IMessageQueue<T> : IMessageQueue { }
      
      class ActiveMQHandler<T> : ActiveMQHandler, IMessageQueue<T> {
      
          public string targetType => typeof(T).ToString();
      }
      

      有了这个,你应该能够注入这样的东西:AddTransient(typeof(IMessageQueue&lt;&gt;), typeof(ActiveMQHandler&lt;&gt;))。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2020-02-05
        • 2020-08-21
        • 1970-01-01
        • 1970-01-01
        • 2016-05-22
        • 1970-01-01
        • 1970-01-01
        • 2020-01-25
        相关资源
        最近更新 更多