[英]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
本身注入到您的類中,然后調用其GetRequiredService
或GetService
方法。 我不這樣做的原因是因為它被認為是一種反模式。 使用工廠允許您仍然在組合根(即在Main
中)進行更改,而無需編輯創建B
實例的任何地方。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.