1. 程式人生 > >探索 .NET Core 依賴注入的 IServiceProvider

探索 .NET Core 依賴注入的 IServiceProvider

在上一篇文章中,我們學習了`Microsoft.Extensions.DependencyInjection`中的IServiceCollection,包括服務註冊轉換為ServiceDescriptors,然後新增到集合中。 ![](https://blog-1259586045.cos.ap-shanghai.myqcloud.com/clipboard_20210217_093425.png) [探索 .NET Core 依賴注入的 IServiceCollection](https://www.cnblogs.com/myshowtime/p/14409907.html "探索 .NET Core 依賴注入的 IServiceCollection") 在本文中,我們會學習 IServiceProvider,瞭解它是什麼,以及它是怎麼創建出來的,我們將根據上一篇文章中建立的IServiceCollection來學習如何構建IServiceProvider。 ## 什麼是 IServiceProvider? IServiceProvider會根據程式的要求在執行時解析服務型別的例項,ServiceProvider來保證已解析的服務在預期的生命週期內有效,這個實現設計的非常高效,所以服務的解析速度非常快。 ## 構建一個 IServiceProvider 首先,當我們把服務都新增到 IServiceCollection ,接下來會構建一個IServiceProvider, 它能夠提供我們程式中所依賴服務的例項,本質上它包裝了 IServiceCollection。 通過呼叫 BuildServiceProvider(IServiceCollection上的一個擴充套件方法)完成構建: ```csharp var serviceCollection = new ServiceCollection(); serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); var serviceProvider = serviceCollection.BuildServiceProvider(); ``` 當我們沒有傳入任何引數時,它會建立一個 ServiceProviderOptions 的一個預設例項: ```csharp public static class ServiceCollectionContainerBuilderExtensions { public static ServiceProvider BuildServiceProvider(this IServiceCollection services) { return services.BuildServiceProvider(ServiceProviderOptions.Default); } ``` ServiceProviderOptions 有兩個屬性,在本文後邊的內容,我會詳細介紹這些: ```csharp public class ServiceProviderOptions { public bool ValidateScopes { get; set; } public bool ValidateOnBuild { get; set; } } ``` BuildServiceProvider 的方法內部是這樣的: ```csharp 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 !NETCOREAPP engine = new DynamicServiceProviderEngine(services); #else if (RuntimeFeature.IsDynamicCodeCompiled) { engine = new DynamicServiceProviderEngine(services); } else { // Don't try to compile Expressions/IL if they are going to get interpreted engine = new RuntimeServiceProviderEngine(services); } #endif return new ServiceProvider(services, engine, options); } ``` 最終,它會建立並返回一個 ServiceProvider。 ## ServiceProviderEngine 在上面的程式碼中,ServiceProvider選擇應該使用哪個 engine, engine 是一個元件,它的功能是負責 DI容器中服務例項的建立,然後把例項注入到其他服務中。 這些是 IServiceProviderEngine 的四個實現: - Dynamic - Runtime - ILEmit - Expressions (System.Linq.Expressions) 從上面的程式碼中,我們可以看到在大多數情況下會使用 DynamicServiceProviderEngine,僅在目標框架不支援動態程式碼編譯的情況下,才使用RuntimeServiceProviderEngine,DynamicServiceProviderEngine 會使用 ILEmit 或者 Expressions 來解析服務。 我們看一下 ServiceProviderEngine 的建構函式的內容: ```csharp protected ServiceProviderEngine(IEnumerable 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>(); } ``` 它建立一個 Root ServiceProviderEngineScope,然後傳入this, scopes限制了服務的生命週期,最常見的就是,.Net Core 收到一個介面請求時,它建立的服務就是 Scope 型別。 這種情況下,我們註冊的單例服務,它都是從 Root Scope 返回的。 然後建立一個 CallSiteRuntimeResolver,我會在接下來的文章介紹它。 最後,在上面的建構函式中,將建立一個新的ConcurrentDictionary來儲存有關服務的資訊,按需設計,只有開始使用這些服務時,它才會開始建立,如果有些服務註冊了,但是沒有使用的話,那麼它永遠不會建立。 ## ServiceProvider 構造方法 讓我們回到 BuildServiceProvider 方法的最後一行,它會傳入 IServiceCollection, Engine和ServiceProviderOptions: ```csharp internal ServiceProvider(IEnumerable serviceDescriptors, IServiceProviderEngine engine, ServiceProviderOptions options) { _engine = engine; if (options.ValidateScopes) { _engine.InitializeCallback(this); _callSiteValidator = new CallSiteValidator(); } if (options.ValidateOnBuild) { List exceptions = null; foreach (ServiceDescriptor serviceDescriptor in serviceDescriptors) { try { _engine.ValidateService(serviceDescriptor); } catch (Exception e) { exceptions = exceptions ?? new List(); exceptions.Add(e); } } if (exceptions != null) { throw new AggregateException("Some services are not able to be constructed", exceptions.ToArray()); } } } ``` 在上面的程式碼中,我們可以看到在建構函式中使用了ServiceProviderOptions, 當ValidateScopes為true時,ServiceProvider會傳入this呼叫 engine 的 InitializeCallback方法,它還建立一個新的CallSiteValidator。 如果 ValidateOnBuild 為true的話,它會檢查DI容器中已註冊的所有服務,遍歷了ServiceDescriptor 集合,然後呼叫 ValidateService, 檢查服務,並且這裡捕獲了異常,如果有錯誤,會丟擲一個聚合的異常資訊。 那麼在程式中使用 ValidateOnBuild,可以保證在程式啟動時就檢查已註冊的錯誤服務,而不是在首次解析服務時在執行時捕獲異常,這個可以很好的幫助排除問題。 ValidateService 的方法內部如下: ```csharp 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); } } ``` ## 總結 在本文中,我們重點介紹瞭如何從IServiceCollection來構建IServiceProvider,我們探索了一些實現細節,以瞭解如何應用ValidateScopes和ValidateOnBuild ServiceProviderOptions,我們在這篇文章中談到了很多內部程式碼,但作為庫的使用者,您不必擔心這些細節。 最重要的一點是,在IServiceCollection上呼叫BuildServiceProvider之後,將建立預設的ServiceProvider。 ```csharp var serviceProvider = serviceCollection.BuildServiceProvider(); ``` 也可以傳入 ServiceProviderOptions ```csharp 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]("https://www.stevejgordon.co.uk/aspnet-core-dependency-injection-what-is-the-iserviceprovider-and-how-is-it-built") ### 最後 歡迎掃碼關注我們的公眾號 【全球技術精選】,專注國外優秀部落格的翻譯和開源專案分享,也可以新增QQ群 897216102