如何在 class 库中使用依赖注入?

How to use Dependency Injection in a class library?

我已经将 Microsoft 依赖注入安装到 .NET Framework 4.8 class 库中。我还注册了接口 A 到 class A。问题是当我尝试创建 class?那么如何让 DI 系统在没有真正入口点的 class 库中提供这个对象?

public Class B
{
   B(){InterfaceA}
}

public Class A : InterfaceA
{}

public MainClass
{
    public void DoStuff()
    {
       DependencyContainer.Register();
       var myB = new B();
    }
}

public DependencyContainer
{
     public void Register()
     {
         (new ServiceCollection()).AddTransient<InterfaceA, A>();
     }
}

此致

好的,所以依赖注入并不神奇。您的 Register 方法正在构建服务集合并将其丢弃。你需要真正从容器中解析你的组件(我不是直接说,但容器必须参与)。

您应该使用 BuildServiceProvider 方法从中构建一个 IServiceProvider

var serviceCollection = new ServiceCollection();
serviceCollection.AddTransient<InterfaceA, A>();
serviceCollection.AddTransient<B>();
var serviceProvider = serviceCollection.BuildServiceProvider();
var b = serviceProvider.GetRequiredService<B>(); // constructs B and injects A

可能您应该在 Main 方法中执行此操作,然后将 B(或创建 B 的 Func)注入 MainClass:

static void Main(string[] args)
{
    var serviceCollection = new ServiceCollection();
    serviceCollection.AddTransient<MainClass>();
    serviceCollection.AddTransient<InterfaceA, A>();
    serviceCollection.AddTransient<B>();
    serviceCollection.AddTransient<Func<B>>(provider => () => provider.GetRequiredService<B>()); // factory method
    var serviceProvider = serviceCollection.BuildServiceProvider();

    // ask the container for MainClass and then call DoStuff
    var mainClass = serviceProvider.GetRequiredService<MainClass>();
    mainClass.DoStuff();
}

然后你可以像这样重写 MainClass:

public class MainClass
{
    private readonly B _bInstance;
    private readonly Func<B> _bFactory = new Func<B>();
    
    // if you want to create instances of B as and when, I'd suggest using a factory
    // otherwise you can pass in a single instance
    // I've done both here as an example
    public MainClass(Func<B> bFactory, B bInstance)
    {
        _bInstance = bInstance;
        _bFactory = bFactory;
    }

    public void DoStuff()
    {
        // create two instances of B
        var b1 = _bFactory();
        var b2 = _bFactory();
    }
}

虽然我提倡在您需要动态构建实例时使用工厂,但可以将 IServiceProvider 自身注入您的 类,然后调用它的 GetRequiredServiceGetService 方法。我不这样做的原因是因为它是 considered to be an anti-pattern。使用工厂允许您仍然在组合根(即 Main 中)进行更改,而无需在创建 B.

实例的任何地方进行编辑

DI 意味着任何依赖项都将从外部进入(注入)。 class 本身甚至不知道使用了依赖注入。在您的情况下,应该重写 classes 以接受依赖项而不是创建它们:

public Class B
{
   public A MyA {get;}

   public B(InterfaceA a)
   {
      MyA=a;
   }
}

public Class A : InterfaceA
{}

public MainClass
{
    public B MyB {get;}
    public MainClass(B b)
    {
        MyB=b;
    }

    public void DoStuff()
    {
       MyB......;
    }
}

class他们对 DI 一无所知。当请求 MainClass 实例时,例如 services.GetRequiredService<MyClass>(),DI 容器将创建所有必要的实例并将它们传递给 classes。

所有 .NET Core 库的通用模式是提供可用于在主应用程序的 Startup class 或 ConfigureServices代表。为此,只需要 Microsoft.Extensions.DependencyInjection.Abstractions 包中的 IServicesCollection 接口。无需添加完整的 DI 包:

public static class MyLibServiceExtensions
{
    public static IServicesCollection AddMyClasses(this IServicesCollection services)
    {
        services.AddTransient<ClassB>()
                .AddTransient<InterfaceA,ClassA>()
                .AddTransient<MainClass>();
        return services;
    }
}

现在可以使用它来注册 classes,例如在控制台应用程序中:

public class Program
{
    public static void Main(string[] args)
    {
        CreateHostBuilder(args).Build().Run();
    }

    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureServices((hostContext, services) =>
            {
                services.AddMyClasses()
                        .AddHostedService<Worker>();
            });
}

或者网络应用的Startup.ConfigureServices方法:

public class Startup
{
    public Startup(IWebHostEnvironment env)
    {
        HostingEnvironment = env;
    }

    public IWebHostEnvironment HostingEnvironment { get; }

    public void ConfigureServices(IServiceCollection services)
    {
        ...

        services.AddMyClasses();
    }
}

classes 现在将被注入到 DI 容器生成的任何 class 中,例如 `Controller:

public class MyController:ControllerBase
{
    public MyController(MyDbContext dbContext,MainClass main)
    {
        _db=dbContext;
        _main=main;
    }
}