我尝试寻找简单的解决方案来将依赖项注入到类库中。
我尝试用一个简单的解决方案来解决这个问题,看起来效果很好。
这就是我所做的。
namespace CompositionRoot
{
public static class Dependencies
{
public static IServiceProvider? ServiceProvider = null;
}
}
IServiceCollection services = new ServiceCollection();
IScriptRepository scriptRepository = new ScriptRepository(
sensorDataConnectionString, "scripts", ControllerName, _logger);
services.AddSingleton(scriptRepository);
IServiceProvider serviceProvider = services.BuildServiceProvider();
CompositionRoot.Dependencies.ServiceProvider = serviceProvider;
private readonly IScriptRepository _scriptRepository;
public BuildingController()
{
_scriptRepository= CompositionRoot.Dependencies.ServiceProvider!.GetService<IScriptRepository>()!;
}
我一直读到这可能是一种“反模式”,但我不确定到底为什么。
这种方法有哪些潜在问题?
使用静态字段使
IServiceProvider
可以全局访问,就像您在第一个代码示例中所做的那样,是服务定位器反模式的实现。通常不鼓励这样做,本文中解释了这样做的原因。
除了该文章中给出的论点之外,对于 MS.DI,问题变得非常明显,因为从根服务提供者进行解析会导致各种令人讨厌的错误,例如强制依赖项和内存泄漏,尤其是在以下应用程序中:基于请求。换句话说,对于您(或代表您的框架)创建
IServiceScope
范围的应用程序。一般来说,对于 MS.DI,服务应该从一个范围解析 - 而不是根容器,但是静态 ServiceProvider
字段不容易允许代码从正确的“范围”服务提供者解析。
从应用依赖注入模式和实践的角度来看,在应用程序的启动路径(您的 Main)中注册库的组件是明智的做法。这个 Main is 你的Composition Root。组合根是应用程序中的“集中”位置,其中注册了所有依赖项并组合了所有对象图。您不想将此逻辑分散在整个应用程序中,也不想让库或项目本身控制此注册和组合。再说一遍,组合根是该去的地方。 请注意,在这种情况下,“集中”并不意味着“所有人都可以访问”,而是“分组在一起”。由于组合根应该位于应用程序的启动项目中,因此组合根之外的任何代码都不应访问它。组合根内部的代码依赖于应用程序中的
所有内容,而组合根之外之外的所有内容(这将是应用程序的 99%)都不会注意到组合根的存在。 重要提示:
虽然您在示例中使用术语CompositionRoot
,但该示例显示了与组合根模式相反的情况。在您的示例中,Dependencies
类是共享的——所有人都可以访问——并且,正如我上面指出的,它充当服务定位器。
如果您想更深入地了解 DI 及其模式(例如组合根)及其反模式(例如服务定位器),请首先浏览本文,即该文章底部的链接。那篇文章和其他两篇参考文献摘自我的书《依赖注入原理、实践和模式》,显然,该书包含的信息比仅在文章中提供的信息要多得多,更不用说 StackOverflow 的答案了。