【问题标题】:What's the difference between the ApplicationServices (root) IServiceProvider and an injected IServiceProvider?ApplicationServices(根)IServiceProvider 和注入的 IServiceProvider 有什么区别?
【发布时间】:2020-05-29 07:26:20
【问题描述】:

当使用IApplicationBuilder.ApplicationServices 而不是IServiceProvider 时,我很难理解为什么在启动配置方法中应用服务定位器模式的行为会有所不同。

当我使用IApplicationBuilder.ApplicationServices设置提供对应用程序的服务容器访问权限的IServiceProvider)时,我收到以下错误:

无法从根提供商解析范围服务“...”

相比之下,当我直接使用 injected IServiceProvider 时,它就像一个魅力:

public void Configure(
    IApplicationBuilder app, IWebHostEnvironment env, IServiceProvider sp)
{
  // 1 : resolving a scoped service directly from application services container
  // IServiceProvider throws 'Cannot resolve scoped service from root provider'
  var test1 = app.ApplicationServices.GetRequiredService<IMyScopedService>();

  // 2 : works like a charm 
  var test2 = sp.GetRequiredService<IMyScopedService>();

  // 3 : also works like a charm
  var scope = app.ApplicationServices.CreateScope();
  var test3 = scope.ServiceProvider.GetRequiredService<IMyScopedService>();
}

这是为什么呢?为什么注入的IServiceProvider 表现得像是具有“服务范围”而IApplicationBuilder.ApplicationServices 表现得像是具有服务范围之外的某种“应用范围”?

在某种程度上注入的IServiceProvider 的工作方式与首先使用IApplicationBuilder 创建一个范围,然后才从范围的服务提供者解析范围内的服务相同

我现在头晕目眩。

显然IApplicationBuilder 的范围(有人这样命名吗?)似乎超出了服务范围。但是当我查找 IApplicationBuilder 时,我找不到任何关于根提供程序的信息。

谁能澄清这一点?我是否错过了一个明显的初学者教程来解释这个根提供程序或应用程序的服务容器?

【问题讨论】:

  • 你是如何注册你的IMyScopedService的?
  • 我猜想为 Configure() 创建了一个作用域,而 IServiceProvider 引用了它
  • 也许,但他定义了对象的生命周期?
  • @Nenad 在 Configureservices 中注册为:services.AddScoped();
  • 我发现你表现出的行为非常奇怪。 Configure 方法是 Startup 的一部分,并且只调用一次。因此,我认为这种方法类似于“单例”。 .ApplicationServices 是根容器这一事实加强了这个想法。因此,我希望 only 单例可以注入到Configure并且注入的IServiceProvider 也将是根。考虑到注入的IServiceProvider 被处理掉了,这可能不是bug;但这肯定是我不喜欢从我的 DI 容器中看到的行为。

标签: c# asp.net-core dependency-injection


【解决方案1】:

这是设计使然。

ASP.NET 运行时实例化 IServiceProvider 以禁止从根解析的作用域服务:

serviceCollection.BuildServiceProvider(validateScopes: true);

app.ApplicationServices 是根IServiceProviderspapp.ApplicationServices 的孩子。

您可以在 Visual Studio 的 Immediate Window 中使用以下代码行检查这一点,同时在断点处(这样您可以直接访问内部 ServiceProviderEngineScope):

app.ApplicationServices == ((Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngineScope)sp).Engine.Root
// true

所以只有app.ApplicatoinServices 的子级可以解析范围服务。提供了一个子 sp 作为输入参数,第二个是您手动创建的。

更新: 我对 MVC Core ServiceProviderScope 默认行为 here 给出了非常详细的答案。

【讨论】:

  • 不幸的是,这在 dotnet 5 中不起作用 - 微软显然决定将 ServiceProviderEngineScope 设为内部
  • @nover ServiceProviderEngineScope 在 dotnet 3 中也是内部的。如果你想检查它,你必须在调试期间执行Immediate Window 中的那一行代码。我在答案中添加了额外的描述。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-04-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-07-11
  • 2014-11-06
相关资源
最近更新 更多