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
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...