【导读】最近重构部分代码,因历史原因在静态类中需使用注入实例,构造函数注入则不再可取,此时只能构造全局IServiceProvider,所以本文稍微分析下IServiceProvider
要构造全局使用IServiceProvider,我们都知道不能在ConfigureServices方法中直接调用BuildServideProvider而获取,会提示引起额外的复制等等问题,这里就不用多说 。
构造全局IServiceProvider
首先我们给出对应接口以及具体实现
public interface IHelloService
{void SayHello();
}public class HelloService : IHelloService
{public void SayHello(){Console.WriteLine("Hello");}
}
接着我们将其注入为单例形式
services.AddSingleton<IHelloService, HelloService>();
最后我们在Configure方法中获取注入实例并调用实例方法,如下:
var serviceProvider = app.ApplicationServices;var service = serviceProvider.GetRequiredService<IHelloService>();
service.SayHello();
看上述调用结果,没有任何毛病,接下来我们将其注入生命周期修改为Scope看看
此时将抛出如上异常,这里我将具体详细信息给出,如下:
Cannot resolve scoped service '...' from root provider
要是我们将生命周期修改为Transient,那么结果和Singleton一样可正常调用。看到上述异常信息,网上部分资料并没有说明根本原因,只是给出如下解决方案
serviceCollection.BuildServiceProvider(validateScopes: false);
接下来我们依然保持上述生命周期Scope不变,我们在Configure方法中传入IServiceProvider参数,结果会怎样呢?
此时我们将发现结果却能正常调用,并不会如上述使用属性而抛出异常,这二者到底有何区别?
依稀记得在使用.NET Core 2.x版本时也遇到过这问题,当时也是想都没想,直接进行如上配置大方的解决了问题。那么为何进行如上设置就可以了呢?抛异常的根本原因在哪里?
这是因为通过属性获取的是根部的IServiceProvider即(root IServiceProvider),而上述以方法参数传入的则是根部的孩子(child of the root)。
通过看这一块源码可通过如下代码来证明参数所传入的IServiceProvider的根部就是app.ApplicationServices
((Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngineScope)serviceProvider).Engine.Root
如下为上述类截图源码,由于上述类属于程序集内并未对外暴露,所以想要验证的童鞋可能只能下载依赖注入这块源码去验证才可
分析到这里,想必我们已经明了,只能通过IServiceProvider子级才能解析注入的Scope实例,因此要从根本上解决通过属性去获取到注入Scope实例,我们还需手动创建子级才可,如下:
var manualScope = app.ApplicationServices.CreateScope();var service = manualScope.ServiceProvider.GetRequiredService<IHelloService>();
service.SayHello();
???? 属性获取的是根部的IServiceProvider即(root IServiceProvider),而以方法参数传入的则是根部的孩子(child of the root)。
???? 属性需创建子级IServiceProvider才可解析Scope服务