【问题标题】:Dependency Injection in .NET Core inside a class library类库中 .NET Core 中的依赖注入
【发布时间】:2020-08-11 04:19:43
【问题描述】:

如何在 .NET Core 库项目中将一个类注入到另一个类中? 我应该在哪里配置 DI,因为它在 API 项目的 StartUp Class ConfigureServices 中完成?

【问题讨论】:

  • 让类显式依赖于另一个(更好的是,它的抽象),然后在组合根目录配置容器。 (启动)
  • 类库中没有启动类。
  • 正如@Nkosi 所说,您的库不应该关注依赖注入,而应该关注控制反转,即将其依赖关系外部化,以便它们可以被注入。配置 DI 容器的工作是针对使用库的应用程序。如果有很多服务需要配置,你可以通过在你的库中添加一个IServiceCollection 扩展来抽象它,但实际上应该是应用程序调用它。

标签: asp.net-core .net-core dependency-injection class-library


【解决方案1】:

关于如何管理它有很多思考过程,因为最终,调用者需要为您注册您的 DI 流程。

如果您查看 Microsoft 和其他公司使用的方法,您通常会使用诸如“AddMyCustomLibrary”之类的方法定义的扩展方法作为 IServiceCollection 的扩展方法。对此here有一些讨论。

【讨论】:

    【解决方案2】:

    您可以使用托管启动程序集类库作为从调用程序集显式注册它们的替代方法。

    https://docs.microsoft.com/en-us/aspnet/core/fundamentals/host/platform-specific-configuration?view=aspnetcore-3.1#class-library

    [assembly: HostingStartup(typeof(HostingStartupLibrary.ServiceKeyInjection))]
    namespace HostingStartupLibrary
    {
        public class Startup : IHostingStartup
        {
            public void Configure(IWebHostBuilder builder)
            {
                builder.ConfigureServices((context, services) => {
                    services.AddSingleton<ServiceA>();
                });
            }
        }
    }
    

    【讨论】:

    • 单例生命周期是整个应用程序,从启动到结束。使用单例不是解决方案!
    • @AliBorjian 我说你只使用单例吗?我刚刚举了一个例子,如何使用依赖注入是单独的类库。
    【解决方案3】:

    在谷歌上搜索了很多之后,我找不到这个问题的例子的全面答案。这是在类库中使用 DI 应该做的事情。

    在您的图书馆中:

    public class TestService : ITestService
    {
        private readonly ITestManager _testManager;
    
        public TestService(ITestManager testManager)
        {
            _testManager = testManager;
        }
    }
    
    public class TestManager : ITestManager 
    {
        private readonly ITestManager _testManager;
    
        public TestManager()
        {
        }
    }
    

    然后在库中扩展 IServiceCollection:

    public static class ServiceCollectionExtensions
    {
        public static void AddTest(this IServiceCollection services)
        {
            services.AddScoped<ITestManager, TestManager>();
            services.AddScoped<ITestService, TestService>();
        }
    }
    

    最后在主应用启动(API、控制台等):

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddTest();
        }
    

    【讨论】:

    • Startup 并没有在类库中被调用。
    • 启动在 API/Console 项目中。它假设调用库
    • @Dazhush 可以说我的解决方案有一个 web api 项目和一个控制台应用程序,如果我的控制台应用程序想要使用这个类库,那么控制台应用程序应该在使用这个类库中的任何类时传入 IConfiguration与 DI?
    • @user1066231 是的。您可以将所需的任何内容传递给 AddTest 方法(包括 IConfiguration)。
    【解决方案4】:

    依赖注入配置在组合根,基本上是应用程序入口点。如果您无法控制应用程序入口点,则不能强制任何人对您的类库使用依赖注入。但是,您可以使用基于接口的编程并创建帮助类来为各种组合根场景注册库中的每种类型,这将允许人们使用 IOC 来实例化您的服务,而不管他们正在创建什么类型的程序。

    你可以做的是让你的类库中的服务依赖于你的库中其他服务的接口,这样使用它们的自然方法就是将你的服务注册到正在使用的容器中,并且还可以提高效率单元测试。

    【讨论】:

      【解决方案5】:

      我不确定我是否完全理解你的意图......但也许你可以让你的实现旋转自己的私有ServiceProvider,如下所示:

      using System.IO;
      
      using Microsoft.Extensions.Configuration;
      using Microsoft.Extensions.DependencyInjection;
      
      public class MyBlackBox {
        private readonly IServiceProvider _services = BuildServices();
      
        protected MyBlackBox() {}
      
        public static MyBlackBox Create() {
          return _services.GetRequiredService<MyBlackBox>();
        }
      
        private static void ConfigureServices(IServiceCollection services) {
          services.AddTransient<MyBlackBox>();
      
          // insert your dependencies here
        }
      
        private static IServiceProvider BuildServices() {
          var serviceCollection = new ServiceCollection();
          serviceCollection.AddLogging();
          serviceCollection.AddOptions();
      
          serviceCollection.AddSingleton(config);
          serviceCollection.AddSingleton<IConfiguration>(config);
      
          ConfigureServices(serviceCollection);
      
          return serviceCollection.BuildServiceProvider();
        }
      
        private static IConfigurationRoot BuildConfig() {
          var path = Directory.GetCurrentDirectory();
          var builder = new ConfigurationBuilder().SetBasePath(path).AddJsonFile("appsettings.json");
          return builder.Build();
        }
      }
      

      然后您可以在“父”ServiceProvider 上注册您的实现,而您的依赖项将不会在其上注册。

      缺点是您必须重新配置所有内容,主要是日志记录和配置。

      如果您需要从父 ServiceProvider 访问某些服务,您可以创建一些东西将它们绑定在一起:

      public static void BindParentProvider(IServiceProvider parent) {
        _services.AddSingleton<SomeService>(() => parent.GetRequiredService<SomeService>());
      }
      

      不过,我很确定有更好的方法来创建嵌套的 ServiceProviders。

      【讨论】:

        【解决方案6】:

        您可以查看 ServiceCollection 扩展模式。

        https://dotnetcoretutorials.com/2017/01/24/servicecollection-extension-pattern/

        如果你在类库中编写这个扩展,你可以在其中注入类/服务。

        但我不知道这是一个很好的模式吗?

        【讨论】:

          猜你喜欢
          • 2023-02-24
          • 1970-01-01
          • 2021-07-15
          • 1970-01-01
          • 1970-01-01
          • 2016-05-22
          • 1970-01-01
          • 2020-11-20
          • 2021-09-14
          相关资源
          最近更新 更多