我正在开发一个SDK,并且对我的类及其依赖项使用DI(我认为不正确)。我相信我将DI和服务定位器混在一起了。我的问题是:
这DI via Builder equivalent to service locator anti-pattern?似乎是我所追求的,但未得到解答。
我使用.NET DI定义容器:
public class ServiceProvider
{
private Microsoft.Extensions.DependencyInjection.ServiceProvider serviceProvider;
protected readonly IServiceCollection serviceCollection = new ServiceCollection();
public ServiceProvider()
{
serviceCollection.AddScoped<IScopedService, ScopedService>();
serviceCollection.AddSingleton<ISingletonService, SingletonService>();
}
}
现在,当ScopedService依赖SingletonService时,可以通过适当的构造函数注入来完成:
public ScopedService(ISingletonService singletonService)
{
_singletonService = singletonService
}
但是,消费类(最终用户)是通过服务定位器完成的,例如
private IScopedService _scopedService;
private IScopedService ScopedService
{
get
{
return _scopedService ?? _scopedService = serviceProvider.GetService<IScopedService>();
}
}
我的所有消耗类都继承自基类,我相信每次实例化一个类时,基类都会创建ServiceProvider的新实例。
public abstract class ComponentBase
{
protected internal ServiceProvider serviceProvider = new ServiceProvider();
}
问题:正在创建多个Singleton对象
var singleton1 = consumingClass.serviceProvider.GetService<ISingletonService>();
var singleton2 = consumingClass.serviceProvider.GetService<ISingletonService>();
Assert.AreEqual(singleton1, singleton2) // Test failure
我希望Singleton服务是同一实例。我应该将基类中的ServiceProvider设为静态吗?这将解决此问题,但在服务定位器反模式的方向上似乎还有更多趋势。
我应该转向工厂模式吗?我在服务提供商中注册所有使用类的地方,只有工厂知道DI容器?然后您可以通过在工厂中调用吸气剂来上课?
答案 0 :(得分:2)
我建议不要在SDK中使用自己的IServiceProvider
,类似于链接文章中所述。
相反,公开SDK的扩展点以插入使用者的DI
public static MySDKServiceCollectionExtensions {
public static IServiceCollection AddMySDK(this IServiceCollection services) {
services.AddScoped<IScopedService, ScopedService>();
services.AddSingleton<ISingletonService, SingletonService>();
//...additional services.
return services
}
}
如果您的使用方组件需要访问这些服务,则通过构造函数注入显式使用依赖原则
public abstract class ComponentBase {
protected ISingletonService singleton;
public ComponentBase(ISingletonService singleton) {
this.singleton = singleton;
}
}
SDK中的IServiceProvider
被视为实现问题,与您的SDK无关。
DI容器仅应从成分根引用。所有其他模块都不应引用该容器。
使用中的项目/应用程序(最终用户)会将您的SDK添加到其合成根目录中
//...
IServiceCollection serviceCollection = new ServiceCollection();
//...Add SDK
serviceCollection.AddMySDK();
//Add my services.
serviceCollection.AddScoped<IConsumer, Consumer>()
//...
并根据需要使用它
public class Consumer: IConsumer {
protected internal IScopedService service;
protected Consumer(IScopedService service) {
this.service = service
}
private IScopedService ScopedService {
get {
return service;
}
}
//...
}