探索 .NET Core 依赖注入的 IServiceProvider
在上一篇文章中,我們學(xué)習(xí)了Microsoft.Extensions.DependencyInjection中的IServiceCollection,包括服務(wù)注冊(cè)轉(zhuǎn)換為ServiceDescriptors,然后添加到集合中。
探索 .NET Core 依賴注入的 IServiceCollection[1]
在本文中,我們會(huì)學(xué)習(xí) IServiceProvider,了解它是什么,以及它是怎么創(chuàng)建出來的,我們將根據(jù)上一篇文章中創(chuàng)建的IServiceCollection來學(xué)習(xí)如何構(gòu)建IServiceProvider。
什么是 IServiceProvider?
IServiceProvider會(huì)根據(jù)程序的要求在運(yùn)行時(shí)解析服務(wù)類型的實(shí)例,ServiceProvider來保證已解析的服務(wù)在預(yù)期的生命周期內(nèi)有效,這個(gè)實(shí)現(xiàn)設(shè)計(jì)的非常高效,所以服務(wù)的解析速度非常快。
構(gòu)建一個(gè) IServiceProvider
首先,當(dāng)我們把服務(wù)都添加到 IServiceCollection ,接下來會(huì)構(gòu)建一個(gè)IServiceProvider, 它能夠提供我們程序中所依賴服務(wù)的實(shí)例,本質(zhì)上它包裝了 IServiceCollection。
通過調(diào)用 BuildServiceProvider(IServiceCollection上的一個(gè)擴(kuò)展方法)完成構(gòu)建:
var serviceCollection = new ServiceCollection(); serviceCollection.AddSingleton<ClassA>(); serviceCollection.AddSingleton<IThing, ClassB>();var serviceProvider = serviceCollection.BuildServiceProvider();當(dāng)我們沒有傳入任何參數(shù)時(shí),它會(huì)創(chuàng)建一個(gè) ServiceProviderOptions 的一個(gè)默認(rèn)實(shí)例:
public static class ServiceCollectionContainerBuilderExtensions { public static ServiceProvider BuildServiceProvider(this IServiceCollection services){return services.BuildServiceProvider(ServiceProviderOptions.Default);}ServiceProviderOptions 有兩個(gè)屬性,在本文后邊的內(nèi)容,我會(huì)詳細(xì)介紹這些:
public class ServiceProviderOptions {public bool ValidateScopes { get; set; }public bool ValidateOnBuild { get; set; } }BuildServiceProvider 的方法內(nèi)部是這樣的:
public static ServiceProvider BuildServiceProvider(this IServiceCollection services, ServiceProviderOptions options) {if (services == null){throw new ArgumentNullException(nameof(services));}if (options == null){throw new ArgumentNullException(nameof(options));}IServiceProviderEngine engine; #if !NETCOREAPPengine = new DynamicServiceProviderEngine(services); #elseif (RuntimeFeature.IsDynamicCodeCompiled){engine = new DynamicServiceProviderEngine(services);}else{// Don't try to compile Expressions/IL if they are going to get interpretedengine = new RuntimeServiceProviderEngine(services);} #endifreturn new ServiceProvider(services, engine, options); }最終,它會(huì)創(chuàng)建并返回一個(gè) ServiceProvider。
ServiceProviderEngine
在上面的代碼中,ServiceProvider選擇應(yīng)該使用哪個(gè) engine, engine 是一個(gè)組件,它的功能是負(fù)責(zé) DI容器中服務(wù)實(shí)例的創(chuàng)建,然后把實(shí)例注入到其他服務(wù)中。
這些是 IServiceProviderEngine 的四個(gè)實(shí)現(xiàn):
?Dynamic?Runtime?ILEmit?Expressions (System.Linq.Expressions)
從上面的代碼中,我們可以看到在大多數(shù)情況下會(huì)使用 DynamicServiceProviderEngine,僅在目標(biāo)框架不支持動(dòng)態(tài)代碼編譯的情況下,才使用RuntimeServiceProviderEngine,DynamicServiceProviderEngine 會(huì)使用 ILEmit 或者 Expressions 來解析服務(wù)。
我們看一下 ServiceProviderEngine 的構(gòu)造函數(shù)的內(nèi)容:
protected ServiceProviderEngine(IEnumerable<ServiceDescriptor> serviceDescriptors) {_createServiceAccessor = CreateServiceAccessor;Root = new ServiceProviderEngineScope(this);RuntimeResolver = new CallSiteRuntimeResolver();CallSiteFactory = new CallSiteFactory(serviceDescriptors);CallSiteFactory.Add(typeof(IServiceProvider), new ServiceProviderCallSite());CallSiteFactory.Add(typeof(IServiceScopeFactory), new ServiceScopeFactoryCallSite());RealizedServices = new ConcurrentDictionary<Type, Func<ServiceProviderEngineScope, object>>(); }它創(chuàng)建一個(gè) Root ServiceProviderEngineScope,然后傳入this, scopes限制了服務(wù)的生命周期,最常見的就是,.Net Core 收到一個(gè)接口請(qǐng)求時(shí),它創(chuàng)建的服務(wù)就是 Scope 類型。
這種情況下,我們注冊(cè)的單例服務(wù),它都是從 Root Scope 返回的。
然后創(chuàng)建一個(gè) CallSiteRuntimeResolver,我會(huì)在接下來的文章介紹它。
最后,在上面的構(gòu)造函數(shù)中,將創(chuàng)建一個(gè)新的ConcurrentDictionary來保存有關(guān)服務(wù)的信息,按需設(shè)計(jì),只有開始使用這些服務(wù)時(shí),它才會(huì)開始創(chuàng)建,如果有些服務(wù)注冊(cè)了,但是沒有使用的話,那么它永遠(yuǎn)不會(huì)創(chuàng)建。
ServiceProvider 構(gòu)造方法
讓我們回到 BuildServiceProvider 方法的最后一行,它會(huì)傳入 IServiceCollection, Engine和ServiceProviderOptions:
internal ServiceProvider(IEnumerable<ServiceDescriptor> serviceDescriptors, IServiceProviderEngine engine, ServiceProviderOptions options) {_engine = engine;if (options.ValidateScopes){_engine.InitializeCallback(this);_callSiteValidator = new CallSiteValidator();}if (options.ValidateOnBuild){List<Exception> exceptions = null;foreach (ServiceDescriptor serviceDescriptor in serviceDescriptors){try{_engine.ValidateService(serviceDescriptor);}catch (Exception e){exceptions = exceptions ?? new List<Exception>();exceptions.Add(e);}}if (exceptions != null){throw new AggregateException("Some services are not able to be constructed", exceptions.ToArray());}} }在上面的代碼中,我們可以看到在構(gòu)造函數(shù)中使用了ServiceProviderOptions, 當(dāng)ValidateScopes為true時(shí),ServiceProvider會(huì)傳入this調(diào)用 engine 的 InitializeCallback方法,它還創(chuàng)建一個(gè)新的CallSiteValidator。
如果 ValidateOnBuild 為true的話,它會(huì)檢查DI容器中已注冊(cè)的所有服務(wù),遍歷了ServiceDescriptor 集合,然后調(diào)用 ValidateService, 檢查服務(wù),并且這里捕獲了異常,如果有錯(cuò)誤,會(huì)拋出一個(gè)聚合的異常信息。
那么在程序中使用 ValidateOnBuild,可以保證在程序啟動(dòng)時(shí)就檢查已注冊(cè)的錯(cuò)誤服務(wù),而不是在首次解析服務(wù)時(shí)在運(yùn)行時(shí)捕獲異常,這個(gè)可以很好的幫助排除問題。
ValidateService 的方法內(nèi)部如下:
public void ValidateService(ServiceDescriptor descriptor) {if (descriptor.ServiceType.IsGenericType && !descriptor.ServiceType.IsConstructedGenericType){return;}try{ServiceCallSite callSite = CallSiteFactory.GetCallSite(descriptor, new CallSiteChain());if (callSite != null){_callback?.OnCreate(callSite);}}catch (Exception e){throw new InvalidOperationException($"Error while validating the service descriptor '{descriptor}': {e.Message}", e);} }總結(jié)
在本文中,我們重點(diǎn)介紹了如何從IServiceCollection來構(gòu)建IServiceProvider,我們探索了一些實(shí)現(xiàn)細(xì)節(jié),以了解如何應(yīng)用ValidateScopes和ValidateOnBuild ServiceProviderOptions,我們?cè)谶@篇文章中談到了很多內(nèi)部代碼,但作為庫(kù)的使用者,您不必?fù)?dān)心這些細(xì)節(jié)。
最重要的一點(diǎn)是,在IServiceCollection上調(diào)用BuildServiceProvider之后,將創(chuàng)建默認(rèn)的ServiceProvider。
var serviceProvider = serviceCollection.BuildServiceProvider();也可以傳入 ServiceProviderOptions
var serviceProviderWithOptions = serviceCollection.BuildServiceProvider(new ServiceProviderOptions {ValidateOnBuild = true,ValidateScopes = true });原文鏈接:?https://www.stevejgordon.co.uk/aspnet-core-dependency-injection-what-is-the-iserviceprovider-and-how-is-it-built[2]
最后
歡迎掃碼關(guān)注我們的公眾號(hào) 【全球技術(shù)精選】,專注國(guó)外優(yōu)秀博客的翻譯和開源項(xiàng)目分享,也可以添加QQ群 897216102
總結(jié)
以上是生活随笔為你收集整理的探索 .NET Core 依赖注入的 IServiceProvider的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 如何在 ASP.Net Core 中对接
- 下一篇: .NET Core用数据库做配置中心加载