ASP.NET Core中的依賴註入(5): ServiceProvider實現揭秘 【解讀ServiceCallSite 】

来源:http://www.cnblogs.com/artech/archive/2016/04/12/asp-net-core-di-service-provider-2.html
-Advertisement-
Play Games

通過上一篇的介紹我們應該對實現在ServiceProvider的總體設計有了一個大致的瞭解,但是我們刻意迴避一個重要的話題,即服務實例最終究竟是採用何種方式提供出來的。ServiceProvider最終採用何種方式提供我們所需的服務實例取決於最終選擇了怎樣的ServiceCallSite,而服務註冊 ...


通過上一篇的介紹我們應該對實現在ServiceProvider的總體設計有了一個大致的瞭解,但是我們刻意迴避一個重要的話題,即服務實例最終究竟是採用何種方式提供出來的。ServiceProvider最終採用何種方式提供我們所需的服務實例取決於最終選擇了怎樣的ServiceCallSite,而服務註冊是採用的ServiceDescriptor有決定了ServiceCallSite類型的選擇。我們將眾多不同類型的ServiceCallSite大體分成兩組,一組用來創建最終的服務實例,另一類則與生命周期的管理有關。

一、用於服務創建的ServiceCallSite

服務實例的創建方式主要有三種,分別對應ServiceDescriptor如下三個只讀屬性。簡單來說,如果ImplementationInstance屬性返回一個具體的對象,該對象將直接作為提供的服務實例。如果屬性ImplementationFactory返回一個具體的委托對象,該委托將會作為提供服務實例的工廠。除此之外,ServiceProvider將會利用ImplementationType屬性返回的真是服務類型定位某一個最佳的構造函數來創建最終提供的服務實例。

   1: public class ServiceDescriptor
   2: {
   3:     public Type                               ImplementationType {  get; }
   4:     public object                             ImplementationInstance {  get; }
   5:     public Func<IServiceProvider, object>     ImplementationFactory {  get; }      
   6: }

服務實例的這三種不同的創建方式最終由三種對應的ServiceCallSite類型來完成,我們將它們的類型分別命名為InstanceCallSite、FactoryCallSite和ConstructorCallSite。如下麵的代碼片段所示,前兩種ServiceCallSite(InstanceCallSite和FactoryCallSite)的實現非常簡單,所以我們在這裡就不對它們多做介紹了。

   1: internal class InstanceCallSite : IServiceCallSite
   2: {
   3: public object Instance { get; private set; }
   4:  
   5:     public InstanceCallSite(object instance)
   6:     {
   7:         this.Instance = instance;
   8:     }
   9:     public Expression Build(Expression provider)
  10:     {
  11:         return Expression.Constant(this.Instance);
  12:     }
  13:     public object Invoke(ServiceProvider provider)
  14:     {
  15:         return Instance;
  16:     }
  17: }
  18:  
  19: internal class FactoryCallSite : IServiceCallSite
  20: {
  21:     public Func<IServiceProvider, object> Factory { get; private set; }
  22:     public FactoryCallSite(Func<IServiceProvider, object> factory)
  23:     {
  24:         this.Factory = factory;
  25:     }
  26:     public Expression Build(Expression provider)
  27:     {
  28:         Expression<Func<IServiceProvider, object>> factory = p => this.Factory(p);
  29:         return Expression.Invoke(factory, provider);
  30:     }
  31:     public object Invoke(ServiceProvider provider)
  32:     {
  33:         return this.Factory(provider);
  34:     }
  35: }

以執行指定構造函數創建服務實例的ConstructorCallSite稍微複雜一點。如下麵的代碼片段所示,我們在創建一個ConstructorCallSite對象的時候除了指定一個代表構造函數的ConstructorInfo對象之外,還需要指定一組用於初始化對應參數列表的ServiceCallSite。

   1: internal class ConstructorCallSite : IServiceCallSite
   2: {
   3:     public ConstructorInfo ConstructorInfo { get; private set; }
   4:     public IServiceCallSite[] Parameters { get; private set; }
   5:  
   6:     public ConstructorCallSite(ConstructorInfo constructorInfo, IServiceCallSite[] parameters)
   7:     {
   8:         this.ConstructorInfo = constructorInfo;
   9:         this.Parameters = parameters;
  10:     }
  11:  
  12:     public Expression Build(Expression provider)
  13:     {
  14:         ParameterInfo[] parameters = this.ConstructorInfo.GetParameters();
  15:         return Expression.New(this.ConstructorInfo, this.Parameters.Select((p, index) => Expression.Convert(p.Build(provider), 
  16:             parameters[index].ParameterType)).ToArray());
  17:     }
  18:  
  19:     public object Invoke(ServiceProvider provider)
  20:     {
  21:         return this.ConstructorInfo.Invoke(this.Parameters.Select(p => p.Invoke(provider)).ToArray());
  22:     }
  23: }

雖然ConstructorCallSite自身創建服務實例的邏輯很簡單,但是如何創建ConstructorCallSite對象本身相對麻煩一些,因為這涉及到如何選擇一個最終構造函數的問題。我們在上面專門介紹過這個問題,並且總結出選擇構造函數採用的兩條基本的策略:

  • ServiceProvider能夠提供構造函數的所有參數。
  • 目標構造函數的參數類型集合是所有有效構造函數參數類型集合的超級。

我們將ConstructorCallSite的創建定義在Service類的CreateConstructorCallSite方法中,它具有額外兩個輔助方法GetConstructor和GetParameterCallSites,前者用於選擇正確的構造函數,後者則為指定的構造函數創建用於初始化參數的ServiceCallSite列表。

   1: internal class Service : IService
   2: {
   3:     private ConstructorCallSite CreateConstructorCallSite(ServiceProvider provider, ISet<Type> callSiteChain)
   4:     {
   5:         ConstructorInfo constructor = this.GetConstructor(provider, callSiteChain);
   6:         if (null == constructor)
   7:         {
   8:             throw new InvalidOperationException("No avaliable constructor");
   9:         }
  10:         return new ConstructorCallSite(constructor, constructor.GetParameters().Select(p => provider.GetServiceCallSite(p.ParameterType, callSiteChain)).ToArray());                                              
  11: }
  12:  
  13:     private ConstructorInfo GetConstructor(ServiceProvider provider, ISet<Type> callSiteChain)
  14:     {
  15:         ConstructorInfo[] constructors = this.ServiceDescriptor.ImplementationType.GetConstructors()
  16:             .Where(c => (null != this.GetParameterCallSites(c, provider, callSiteChain))).ToArray();
  17:  
  18:         Type[] allParameterTypes = constructors.SelectMany(
  19:             c => c.GetParameters().Select(p => p.ParameterType)).Distinct().ToArray();
  20:  
  21:         return constructors.FirstOrDefault(
  22:             c => new HashSet<Type>(c.GetParameters().Select(p => p.ParameterType)).IsSupersetOf(allParameterTypes));
  23:     }
  24:  
  25:     private IServiceCallSite[] GetParameterCallSites(ConstructorInfo constructor,ServiceProvider provider,ISet<Type> callSiteChain)
  26:     {
  27:         ParameterInfo[] parameters = constructor.GetParameters();
  28:         IServiceCallSite[] serviceCallSites = new IServiceCallSite[parameters.Length];
  29:  
  30:         for (int index = 0; index < serviceCallSites.Length; index++)
  31:         {
  32:             ParameterInfo parameter = parameters[index];
  33:             IServiceCallSite serviceCallSite = provider.GetServiceCallSite(
  34:                 parameter.ParameterType, callSiteChain);
  35:             if (null == serviceCallSite && parameter.HasDefaultValue)
  36:             {
  37:                 serviceCallSite = new InstanceCallSite(parameter.DefaultValue);
  38:             }
  39:             if (null == serviceCallSite)
  40:             {
  41:                 return null;
  42:             }
  43:             serviceCallSites[index] = serviceCallSite;

              
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • mysql的性能優化無法一蹴而就,必須一步一步慢慢來,從各個方面進行優化,最終性能就會有大的提升。 Mysql資料庫的優化技術 對mysql優化是一個綜合性的技術,主要包括 表的設計合理化(符合3NF) 添加適當索引(index) [四種: 普通索引、主鍵索引、唯一索引unique、全文索引] 分表 ...
  • 在我們使用查詢語句的時候,經常要返回前幾條或者中間某幾行數據,這個時候怎麼辦呢?不用擔心,Mysql已經為我們提供了這樣一個功能。 SELECT * FROM table LIMIT [offset,] rows | rows OFFSET offset SELECT * FROM table LI ...
  • 今晚繼續進行Sql效能問題的分享,今天主要是一些具體的sql優化方法和思路分享,若看過後你也有其他想法,歡迎一起探討,好了,進入今天的主題。 針對性地對一些耗資源嚴重的具體應用進行優化 出現效能問題時,首先要做的是什麼?這個問題我問過不少同事,有人說憑經驗對出問題的sql進行優化,如我們一般說的要合 ...
  • 今天本想將之前的一個資料庫easy.sql用圖形化工具Navicat導入的,開始是用“運行SQL文件”導入,結果是“queries successfully”啥的,去查是否導表成功,一看並沒有。 結果失望之餘,把那個資料庫和伺服器全刪了,再建一個和要導入的資料庫同名的資料庫名,意外的是重啟後資料庫就 ...
  • 1唯一約束unique和主鍵key的區別? 1、什麼是數據的存儲引擎? 存儲引擎就是如何存儲數據、如何為存儲的數據建立索引和如何更新、查詢數據等技術的實現方法。因為在關係資料庫中數據的存儲是以表的形式存儲的,所以存儲引擎也可以稱為表類型(即存儲和操作該表的類型),在Oracle和SQL Server ...
  • 1概述 1.1 介紹 Valgrind是一套Linux下,開放源代碼(GPL V2)的模擬調試工具的集合。Valgrind由內核(core)以及基於內核的其他調試工具組成。內核類似於一個框架(framework),它模擬了一個CPU環境,並提供服務給其他工具;而其他工具則類似於插件 (plug-in ...
  • 1上下鍵讓文件為選中狀態 2左鍵選擇文件夾,再次左鍵摺疊。 ...
  • 今天,筆者在備份文件的時候,將一個word文檔從移動硬碟複製到桌面。經過一系列“復(meng)雜(bi)”的操作之後,筆者突然發現,文件無法刪除了。當右鍵文件點擊“刪除”時,出現對話框顯示“正在準備 再迴圈”的字樣。 筆者嘗試了許多方法,例如:當出現“正在準備 再迴圈”字樣時,註銷電腦;下載文件粉 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...