簡體   English   中英

如何在 class 庫中使用依賴注入?

[英]How to use Dependency Injection in a class library?

我已將 Microsoft 依賴注入安裝到 .NET Framework 4.8 class 庫中。 I have also reigster interface A to class A. The problem is that my class B that takes a object of interface A in construtor still demands it when I try to create the class? 那么如何讓 DI 系統在沒有真正入口點的 class 庫中提供這個 object 呢?

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

問候

DI 意味着任何依賴都將來自外部(注入)。 class 本身甚至不知道使用了依賴注入。 在您的情況下,應該重寫類以接受依賴項而不是創建它們:

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

這些課程對 DI 一無所知。 當請求MainClass實例時,例如services.GetRequiredService<MyClass>() ,DI 容器將創建所有必要的實例並將它們傳遞給類。

所有 .NET 核心庫的通用模式是提供可用於在主應用程序的Startup class 或ConfigureServices委托中注冊或配置其類的擴展方法。 為此,只需要來自Microsoft.Extensions.DependencyInjection.Abstractions package 的IServicesCollection接口。 無需添加完整的 DI package:

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

現在可以使用它來注冊類,例如在控制台應用程序中:

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

或者 web 應用程序的Startup.ConfigureServices方法:

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

    public IWebHostEnvironment HostingEnvironment { get; }

    public void ConfigureServices(IServiceCollection services)
    {
        ...

        services.AddMyClasses();
    }
}

這些類現在將被注入到由 DI 容器生成的任何 class 中,例如`Controller:

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

好的,所以依賴注入並不神奇。 您的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方法。 我不這樣做的原因是因為它被認為是一種反模式 使用工廠允許您仍然在組合根(即在Main中)進行更改,而無需編輯創建B實例的任何地方。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM