Core官方DI解析(4)--CallSiteRuntimeResolver

来源:https://www.cnblogs.com/yan7/archive/2018/11/29/10036148.html
-Advertisement-
Play Games

​ 類型是一個創建或獲取服務實例的類型,這個類型繼承了 這個類型,也是使用了訪問者模式,下麵一一來解析此類 ServiceProviderEngineScope 在解析 之前先看一下 類型,這個類型就可以是一個容器類型,最後實例化的服務對象就緩存在此類之中, 從下麵代碼中可以看出此類實現了 和`IS ...


CallSiteRuntimeResolver類型是一個創建或獲取服務實例的類型,這個類型繼承了CallSiteVisitor<TArgument, TResult>這個類型,也是使用了訪問者模式,下麵一一來解析此類

ServiceProviderEngineScope

在解析`CallSiteRuntimeResolver`之前先看一下`ServiceProviderEngineScope`類型,這個類型就可以是一個容器類型,最後實例化的服務對象就緩存在此類之中,

從下麵代碼中可以看出此類實現了`IServiceScope`和`IServiceProvider`兩個介面,並且此類型擁有兩個欄位

_disposables:IDisposabl集合,此欄位緩存的時所有實現了IDisposable介面的註冊服務,以便在釋放此容器實例時並將這些服務一起釋放

_disposed:判斷此屬性是否已被是否釋放

internal class ServiceProviderEngineScope : IServiceScope, IServiceProvider
{
       private List<IDisposable> _disposables;
       private bool _disposed;
}
在此類中還具有兩個屬性,一個是緩存實例對象的集合和一個**ServiceProviderEngine**類型的屬性,從下麵可以看出緩存集合使用了是`ServiceCacheKey`作為緩存的key,

而Engine是引擎類型,此屬性通過構造函數傳入,並且所有容器共用一個`ServiceProviderEngine`,也就是共用容器共用註冊的服務   
//    緩存的實例對象集合
internal Dictionary<ServiceCacheKey, object> ResolvedServices { get; } = new Dictionary<ServiceCacheKey, object>();
//    所有ServiceProviderEngineScope對象共用一個ServiceProviderEngine
public ServiceProviderEngine Engine { get; }

//   構造函數
 public ServiceProviderEngineScope(ServiceProviderEngine engine)=> Engine = engine;
這個類中一共具有四個方法,
  • GetService():獲取對象,可以看到此方法調用的EngineGetService(),這個方法到ServiceProviderEngine時再看
  • ServiceProvider():這個方法返回的是當前對象
  • Dispose():釋放當前容器,可以看到在釋放當前容器時會把**_disposables集合中所有實例進行釋放,並把_disposed**屬性設置TRUE
  • CaptureDisposable():這個方法緩存要被的釋放的服務實例
public object GetService(Type serviceType)
{
     if (_disposed)
          //        如果已被釋放,就不能調用此方法
          ThrowHelper.ThrowObjectDisposedException();
     return Engine.GetService(serviceType, this);
}

public IServiceProvider ServiceProvider => this;

public void Dispose()
{
     lock (ResolvedServices)
     {
          if (_disposed)
               return;
          _disposed = true;
          if (_disposables != null)
          {
               for (var i = _disposables.Count - 1; i >= 0; i--)
               {
                    var disposable = _disposables[i];
                    disposable.Dispose();
               }

               _disposables.Clear();
          }

          ResolvedServices.Clear();
     }
}
//  緩存所有需要清理的服務實例
internal object CaptureDisposable(object service)
{

     if (!ReferenceEquals(this, service))
     {
          if (service is IDisposable disposable)
          {
               lock (ResolvedServices)
               {
                    if (_disposables == null)
                         _disposables = new List<IDisposable>();
                    _disposables.Add(disposable);
               }
          }
     }
     return service;
}

CallSiteRuntimeResolver

​ 上面說過CallSiteRuntimeResolver這個類型是創建和獲取服務實例類型的訪問者,這個類型泛型參數分別為RuntimeResolverContext類型和實例對象類型Object

internal sealed class CallSiteRuntimeResolver : CallSiteVisitor<RuntimeResolverContext, object>{}

RuntimeResolverContext類型是一個ServiceProviderEngineScope封裝類型,這個類型中具有一個ServiceProviderEngineScope類型屬性和一個RuntimeResolverLock枚舉類型屬性,這個枚舉類型在實例化對象時當做了鎖使用

internal struct RuntimeResolverContext
{
     public ServiceProviderEngineScope Scope { get; set; }
     //     鎖
     public RuntimeResolverLock AcquiredLocks { get; set; }
}
[Flags]
internal enum RuntimeResolverLock
{
     Scope = 1,
     Root = 2
}

CallSiteRuntimeResolver類型中擁有兩類方法,

  • 根據註冊服務的生命周期進行訪問服務實例對象
  • 根據ServiceCallSite的設置類型進行訪問服務實例對象

這兩個類都在其CallSiteVisitor<TArgument, TResult>基類中

//      根據服務對象的生命周期進行訪問訪問實例
protected virtual TResult VisitCallSite(ServiceCallSite callSite, TArgument argument)
{
     // 緩存位置由ServiceCallSite內部的Cache屬性的Location提供
     switch (callSite.Cache.Location)
     {
          case CallSiteResultCacheLocation.Root:
               return VisitRootCache(callSite, argument);
          case CallSiteResultCacheLocation.Scope:
               return VisitScopeCache(callSite, argument);
          case CallSiteResultCacheLocation.Dispose:
               return VisitDisposeCache(callSite, argument);
          case CallSiteResultCacheLocation.None:
               return VisitNoCache(callSite, argument);
          default:
               throw new ArgumentOutOfRangeException();
     }
}

//      根據其ServiceCallSite的Kind屬性訪問服務對象
protected virtual TResult VisitCallSiteMain(ServiceCallSite callSite, TArgument argument)
{
     switch (callSite.Kind)
     {
          case CallSiteKind.Factory:
               return VisitFactory((FactoryCallSite)callSite, argument);
          case  CallSiteKind.IEnumerable:
               return VisitIEnumerable((IEnumerableCallSite)callSite, argument);
          case CallSiteKind.Constructor:
               return VisitConstructor((ConstructorCallSite)callSite, argument);
          case CallSiteKind.Constant:
               return VisitConstant((ConstantCallSite)callSite, argument);
          case CallSiteKind.ServiceProvider:
               return VisitServiceProvider((ServiceProviderCallSite)callSite, argument);
          case CallSiteKind.ServiceScopeFactory:
               return VisitServiceScopeFactory((ServiceScopeFactoryCallSite)callSite, argument);
          default:
               throw new NotSupportedException($"Call site type {callSite.GetType()} is not supported");
     }
}

​這兩個方法內部調用的方法部分被CallSiteRuntimeResolver類中重寫,

下麵先來看看根據生命周期進行訪問的一系列方法

  • VistRootCache:

    這個方法是訪問Root生命周期的方法,可以看到這個在這個方法調用了一個VisitCache(),這個方法一共四個參數,第一個,第二個分別是當前方法函數。第三個參數代表容器對象,容器使用的是ServiceProviderEngine實例中的Root屬性,這個容器代表了頂級容器,這也就是Root生命周期的本質,使用的頂級容器進行創建/獲取實例,第四個參數鎖,此方法使用的是RuntimeResolverLock.Root

  • VisitScopeCache:

    這個方法是訪問Scoped生命周期方法,此方法和上面方法相似,也是調用了VisitCache(),但是不同的是是鎖不同,這個鎖是根據當前容器來決定,如果當前容器為頂級容器,就使用Root鎖,所以不為頂級容器,則使用Scope

  • VisitDisposeCache

    這個方法訪問transient生命周期方法,可以看到這個方法直接調用VisitCallSiteMain()進行獲取實例對象,然後調用CaptureDisposable()將此對象嘗試緩存到ServiceProviderEngineScope容器的**_disposables**集合中

  • VisitNoCache

    這個方法代表不緩存,這個方法在CallSiteRuntimeResolver類中未重寫,所以直接調用的CallSiteVisitor類型的VisitNoCache(),也基類中直接調用VisitCallSiteMain()


////        CallSiteRuntimeResolver
//      訪問Root生命周期方法
protected override object VisitRootCache(ServiceCallSite singletonCallSite, RuntimeResolverContext context)
      => VisitCache(singletonCallSite, context, context.Scope.Engine.Root, RuntimeResolverLock.Root);
//      訪問Scoped生命周期方法
protected override object VisitScopeCache(ServiceCallSite singletonCallSite, RuntimeResolverContext context)
{
     //      如果當前容器為根容器,則將其鎖轉換為Root,否則為Scope
     var requiredScope = context.Scope == context.Scope.Engine.Root ?
          RuntimeResolverLock.Root :
     RuntimeResolverLock.Scope;
     return VisitCache(singletonCallSite, context, context.Scope, requiredScope);
}
//      訪問transient生命周期方法
protected override object VisitDisposeCache(ServiceCallSite transientCallSite, RuntimeResolverContext context)
      => context.Scope.CaptureDisposable(VisitCallSiteMain(transientCallSite, context));

////        CallSiteVisitor
//      無緩存
 protected virtual TResult VisitNoCache(ServiceCallSite callSite, TArgument argument)
      => VisitCallSiteMain(callSite, argument);
**VisitCache()**這個方法是使用指定的容器進行實例化並緩存服務實例對象,在下麵代碼中可以看到,代碼中根據**RuntimeResolverContext**實例的枚舉值與第四個參數進行,如果不相同,則進行加鎖。然後進行獲取實例服務對象,如果已緩存則直接獲取,沒有緩存則調用**VisitCallSiteMain()**獲取實例並緩存
private object VisitCache(ServiceCallSite scopedCallSite, RuntimeResolverContext context, ServiceProviderEngineScope serviceProviderEngine, RuntimeResolverLock lockType)
{
     bool lockTaken = false;
     //      獲取容器中的緩存服務實例屬性
     var resolvedServices = serviceProviderEngine.ResolvedServices;
     if ((context.AcquiredLocks & lockType) == 0)
         //      如果當前枚舉值與RuntimeResolverContext的枚舉值不相同,則加鎖
          Monitor.Enter(resolvedServices, ref lockTaken);
     try
     {
          //      如果當前數據並未在緩存之中,則實例化此對象並將其緩存至集合中
          if (!resolvedServices.TryGetValue(scopedCallSite.Cache.Key, out var resolved))
          {
               //      獲取實例對象
               resolved = VisitCallSiteMain(scopedCallSite, new RuntimeResolverContext
                                            {
                                                 Scope = serviceProviderEngine,
                                                 AcquiredLocks = context.AcquiredLocks | lockType
                                            });
               //      將當前對象嘗試加入到容器的_disposables集合
               serviceProviderEngine.CaptureDisposable(resolved);
               //      緩存實例對象
               resolvedServices.Add(scopedCallSite.Cache.Key, resolved);
          }
          return resolved;
     }
     finally
     {
          if (lockTaken)
            Monitor.Exit(resolvedServices);
     }
}
**VisitCallSiteMain()**內調用的所有方法都在`CallSiteRuntimeResolver`類進行了重寫,下麵看看`CallSiteRuntimeResolve`類中的這些方法
  • VisitFactory

    VisitFactory()中直接調用了FactoryCallSite實例對象的工廠方法獲取實例

  • VisitIEnumerable

    VisitIEnumerable()中實例了IEnumerableCallSiteServiceCallSites集合的所有對象,並組裝到一個數組進行返回

  • ConstructorCallSite

    VisitConstructor()中使用反射方法實例化對象,並且如果構造函數不為空則獲取所有參數的實例對象

  • ConstantCallSite

    VisitConstant()中直接返回了ConstantCallSite中的對象

  • VisitServiceProvider

    VisitServiceProvider()直接返回了RuntimeResolverContext封裝的容器

  • VisitServiceScopeFactory

    VisitServiceScopeFactory()中則直接返回了容器實例中引擎對象(ServiceProviderEngine)

//      FactoryCallSite
protected override object VisitFactory(FactoryCallSite factoryCallSite, RuntimeResolverContext context)
     //     調用工廠方法進行實例化
     => factoryCallSite.Factory(context.Scope);

//      IEnumerableCallSite
protected override object VisitIEnumerable(IEnumerableCallSite enumerableCallSite, RuntimeResolverContext context)
{
     var array = Array.CreateInstance(
          enumerableCallSite.ItemType,
          enumerableCallSite.ServiceCallSites.Length);
     for (var index = 0; index < enumerableCallSite.ServiceCallSites.Length; index++)
     {
          //        實例化IEnumerableCallSite.ServiceCallSites中所有的服務實例對象並賦值到數組中
          var value = VisitCallSite(enumerableCallSite.ServiceCallSites[index], context);
          array.SetValue(value, index);
     }
     return array;
}

//      ConstructorCallSite
protected override object VisitConstructor(ConstructorCallSite constructorCallSite, RuntimeResolverContext context)
{
     object[] parameterValues;
     if (constructorCallSite.ParameterCallSites.Length == 0)
          parameterValues = Array.Empty<object>();
     else
     {
          //        如果當前構造器參數不為空,則實例化每一個參數的實例對象
          parameterValues = new object[constructorCallSite.ParameterCallSites.Length];
          for (var index = 0; index < parameterValues.Length; index++)
               parameterValues[index] = VisitCallSite(constructorCallSite.ParameterCallSites[index], context);
     }
     try
     {
          //        根據參數對象進行實例化對象並返回
          return constructorCallSite.ConstructorInfo.Invoke(parameterValues);
     }
     catch (Exception ex) when (ex.InnerException != null)
     {
          ExceptionDispatchInfo.Capture(ex.InnerException).Throw();
          // The above line will always throw, but the compiler requires we throw explicitly.
          throw;
     }
}

//      ConstantCallSite
 protected override object VisitConstant(ConstantCallSite constantCallSite, RuntimeResolverContext context)
      //        直接返回ConstantCallSite的值
      => constantCallSite.DefaultValue;

//      ServiceProviderCallSite
protected override object VisitServiceProvider(ServiceProviderCallSite serviceProviderCallSite, RuntimeResolverContext context) 
     //     直接返回RuntimeResolverContext封裝的容器
     => context.Scope;

//      ServiceScopeFactoryCallSite
 protected override object VisitServiceScopeFactory(ServiceScopeFactoryCallSite serviceScopeFactoryCallSite, RuntimeResolverContext context)
      //        直接返回容器內的ServiceProviderEngine
      => context.Scope.Engine;
在`CallSiteRuntimeResolver`中還有叫做**Resolve()**,這個方法則是外部調用的,這個方法是由一個`ServiceCallSite`對象和一個容器對象`ServiceProviderEngineScope`,然後直接調用**VisitCallSite()**進行方法,可以看到調用此方法時**AcquiredLocks**屬性並未賦值.
public object Resolve(ServiceCallSite callSite, ServiceProviderEngineScope scope)
{
     return VisitCallSite(callSite, new RuntimeResolverContext
                          {
                               Scope = scope
                          });
}

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

-Advertisement-
Play Games
更多相關文章
  • DNS DomainNameSystem功能變數名稱系統,根據功能變數名稱查出IP地址 1.dig命令可以顯示整個查詢的過程 root@VM-38-204-ubuntu:~# dig www.sopans.com //這一段是查詢參數和統計 ; > DiG 9.10.3-P4-Ubuntu > www.sopans... ...
  • """提示:代碼中的內容均被註釋,請參考,切勿照搬""" """註意:代碼切勿照搬,錯誤請留言指出""" ...
  • 題意 "題目鏈接" Sol 一開始的思路:新建一個虛點向每個點連邊,再加上題面中給出的邊,邊權均為大小 需要購買的數量 然後發現死活都過不去 看了題解才發現題目中有個細節——買了$A$就可以買$B$,但是人家沒告訴你必須買夠$A$的數量才能買$B$呀qwqqqqqqq 所以建圖的時候只算一次貢獻就行 ...
  • 這裡有兩張表TableA和TableB,分別是姓名錶和年齡表,用於我們例子的測試數據: 表連接有幾種? sql表連接分成外連接、內連接和交叉連接。 一.外連接 概述: 外連接包括三種,分別是左外連接、右外連接、全外連接。 對應的sql關鍵字:LEFT/RIGHT/FULL OUTER JOIN,通常 ...
  • pip安裝使用 i參數指定源 指定豆瓣源,快如閃電:pip install i http://pypi.douban.com/simple/ 使用sys.modules查看導入包的路徑 sys.modules是一個全局字典,可以通過它來瞭解當前環境載入了什麼包,包的路徑 ...
  • Kibana安裝及使用說明 Kibana是一個針對Elasticsearch的開源分析及可視化平臺,用來搜索、查看交互存儲在Elasticsearch索引中的數據。 官方地址:https://www.elastic.co/cn/products/kibana 一、Kibana安裝 1.環境準備 Ki ...
  • 一、什麼是進程 進程(Process)是電腦中的程式關於某數據集合上的一次運行活動,是系統進行資源分配和調度的基本單位,是操作系統結構的基礎。在早期面向進程設計的電腦結構中,進程是程式的基本執行實體;在當代面向線程設計的電腦結構中,進程是線程的容器。程式是指令、數據及其組織形式的描述,進程是程 ...
  • 最近在學習leal的時候遇到了一點非常迷惑的地方,就是leal是用來取有效地址的,但是為什麼它也可以實現賦值呢?偶然發現一個博客講的不錯,遂自己記錄一下 一個這樣的例子 如果寄存器edx裡面存的值為x,我們知道這句結束之後edx裡面的值會被置為5x+7,但是看起來為什麼是值而不是地址呢? 實際上這之 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...