【问题标题】:IServiceProvider.GetSingleton resolving null in Asp.Net Core DIIServiceProvider.GetSingleton 在 Asp.Net Core DI 中解析 null
【发布时间】:2017-05-14 16:59:42
【问题描述】:

在我的应用程序的ConfigureServices() 引导期间,我主要使用 .Net Core 附带的依赖注入框架注册瞬态和作用域类型。但是,我确实有一种注册为单例的类型是我的InProcessBus(我使用的是 CQRS 风格的架构)。

services.AddSingleton<InProcessBus>(new InProcessBus());
...         
services.AddSingleton<ICommandSender>(y => y.GetService<InProcessBus>());
services.AddSingleton<IEventPublisher>(y => y.GetService<InProcessBus>());

如您所见,我正在为将在 API 控制器中使用的实际类型使用实现工厂函数。真正奇怪的是,当控制器加载并且运行时尝试构造函数注入 ICommandSender 时,它无法解决它并报告错误。

如果我检查 serviceCollection,我可以看到 implementationFactory 已针对 ImplementationInstance 属性下的类型正确注册。

深入并直接解析Startup类末尾的类型'ConfigureServices()方法确认容器正在解析null

ServiceLocator.GetService<ICommandSender>() // Is Null

为什么针对容器中明显存在的单例的 ImplementationFactory 方法在运行时无法解析?

【问题讨论】:

  • 这很奇怪。尽管您可能希望避免使用y.GetService&lt;T&gt;() 并使用y.GetRequiredService&lt;T&gt;(),如果无法解决它会抛出异常,而不是返回null。尽管这可能无法解决您的问题。还有更多吗?您也无法在ConfigureServices 中解决它,当时容器尚未构建。您可以解决服务的最早一点是在Configure 方法中(如果您使用模板附带的标准 Startup.cs)
  • 您确定要使用内置容器吗? CQRS 架构风格非常强大,因为它们使使用装饰器应用横切关注点变得非常容易。使用内置容器是不可能应用通用装饰器的。
  • @Tseng,感谢 GetRequiredService 提示,显式异常比 null 好。通常无法解决,但是当我遇到问题时,我使用 BuildServiceProvider() 来构建自己的提供程序以使用内联进行测试...
  • @Steven,我使用自定义跨框架容器合同包装容器,这样我就没有任何特定容器的硬容器依赖。这个特殊的 CQRS 实现是一个非常精简的实现,因为我实际上主要是在玩 .Net Core 和 Standard。
  • @SarelEsterhuizen:你也不会对内置有任何依赖。唯一需要参考的地方是你的作文根目录。在所有其他层中,您只需在类构造函数或IEnumerable&lt;T&gt; 中注入所需服务的接口,如果您需要某种类型的所有注册。 ServiceLocator 只是一个非常糟糕的模式。另请参阅下面的 cmets,您将遇到其他问题,因为您现在可能有两个容器实例,其中一个永远不会被释放。因此,如果使用不当,您的作用域服务可能会成为服务定位器中的单例

标签: .net dependency-injection asp.net-core asp.net-core-mvc .net-core


【解决方案1】:

我发现了哪里出了问题,我想把它贴在这里,因为它有一个重要的教训。

简答:

serviceCollection.BuildServiceProvider() 推迟到您注册了所有单例实例之后,否则它将无法解决它们。

长答案:

这个问题实际上与 asp core DI 容器无关(好吧)。问题在于我在serviceCollection 实例上调用BuildServiceProvider 以向我提供IServiceProvider 的时间。

在我的实现中,我实现了一个 ServiceLocator 模式,它包装了核心附带的 DI 框架,这样我就不会在我的其他架构层中引入额外的依赖项。当我构建基于IServiceContainer 的定位器时,我会立即解析ServiceProvider

public ServiceLocator(
        IServiceCollection serviceCollection)
    {
        _serviceCollection = serviceCollection;
        _serviceProvider = _serviceCollection.BuildServiceProvider();
    } 

这就是问题出现的地方。ServiceProvider 的创建时间对于 TransientsScoped 实例显然无关紧要。 但对于具有托管生命周期的单身人士来说,它确实如此。您需要在完成所有注册后推迟创建 ServiceProviders

根据我的问题,ServiceLocator 是在 sn-ps 之前构造的,这意味着当 implementationFactory 函数执行时,ServiceProvider 将无法访问 Singleton 实例。

通过将 ServiceProvider 的创建推迟到我的应用程序在运行时第一次解析我的一个自定义类型时解决了这个问题。

【讨论】:

  • 您可能会在使用这种方法时遇到其他问题(忽略 Service Locator 是反模式并且超出 DI/IoC 的全部目的这一事实):您的系统中现在将有两个容器,一个ASP.NET Core 在调用 Configure 之前创建的以及您为反模式定位器创建的一个,这对于使用 ASP.NET Core IoC 的服务和使用您的服务定位器的服务范围内的服务 (DbContext) 来说将是一个问题将有不同的实例,例如调用 SaveChanges() 不会保护您的其他上下文中的更改,并且您会失去工作单元优势
  • 您能否详细说明“包装核心附带的 DI 框架的模式,这样我就不会在我的其他架构层中引入额外的依赖项”? Microsoft.Extensions.DependencyInjection 的其他层不需要任何其他依赖项。您只需要在 Web 应用程序中使用它(在组合根中构建对象图)。从架构的角度来看,您的方法似乎非常错误,它会产生硬依赖,并且难以/不可能进行单元测试
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-06-09
  • 1970-01-01
  • 2017-07-28
  • 2019-08-24
  • 1970-01-01
  • 2019-02-15
  • 1970-01-01
相关资源
最近更新 更多