文章出處

通過上一篇的介紹我們應該對實現在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;
  44:         }
  45:         return serviceCallSites;
  46:     }
  47:     //其他成員
  48: }


二、用于管理生命周期的ServiceCallSite

服務實例最終采用何種提供方式還與服務注冊時采用的生命周期管理模式有關,三種不同的生命周期管理模式(Transient、Scoped和Singleton)分別對應著三種不同的ServiceCallSite類型,我們分別將其命名為TransienCallSite、ScopedCallSite和SingletonCallSite。

對于TransientCallSite來說,它提供服務實例的方式非常直接,那就是直接創建一個新的對象。在此之外還需要考慮一個關于服務回收的問題,那就是如果服務實例對應的類型實現了IDisposable接口,需要將它添加到ServiceProvider的TransientDisposableServices屬性中。TransientCallSite具有如下的定義,只讀屬性ServiceCallSite表示真正用于創建服務實例的ServiceCallSite。

   1: internal class TransientCallSite : IServiceCallSite
   2: {
   3:     public IServiceCallSite ServiceCallSite { get; private set; }
   4:     public TransientCallSite(IServiceCallSite serviceCallSite)
   5:     {
   6:         this.ServiceCallSite = serviceCallSite;
   7:     }
   8:  
   9:     public  Expression Build(Expression provider)
  10:     {
  11:         return Expression.Call(provider, "CaptureDisposable", null, this.ServiceCallSite.Build(provider));
  12:     }
  13:  
  14:     public  object Invoke(ServiceProvider provider)
  15:     {
  16:         return provider.CaptureDisposable(this.ServiceCallSite.Invoke(provider));
  17:     }
  18: }
  19:  
  20: internal class ServiceProvider : IServiceProvider, IDisposable
  21: {
  22:     
  23:     public object CaptureDisposable(object instance)
  24:     {
  25:         IDisposable disposable = instance as IDisposable;
  26:         if (null != disposable)
  27:         {
  28:             this.TransientDisposableServices.Add(disposable);
  29:         }
  30:         return instance;
  31:     }
  32:     //其他成員
  33: }

ScopedCallSite在提供服務實例的時候需要考慮當前ServiceProvider的ResolvedServices屬性中是否已經存在已經提供的服務實例,如果已經存在它就無需重復創建。新創建的服務實例需要添加到當前ServiceProvider的ResolvedServices屬性中。ScopedCallSite定義如下,ServiceCallSite屬性依然表示的是真正用于創建服務實力的ServiceCallSite,Service屬性則用來獲取保存在當前ServiceProvider的ResolvedServices屬性中的服務實例。

   1: internal class ScopedCallSite : IServiceCallSite
   2: {
   3:     public IService Service { get; private set; }
   4:     public IServiceCallSite ServiceCallSite { get; private set; }
   5:  
   6:     public ScopedCallSite(IService service, IServiceCallSite serviceCallSite)
   7:     {
   8:         this.Service = service;
   9:         this.ServiceCallSite = serviceCallSite;
  10:     }
  11:  
  12:     public virtual Expression Build(Expression provider)
  13:     {
  14:         var service = Expression.Constant(this.Service);
  15:         var instance = Expression.Variable(typeof(object), "instance");
  16:         var resolvedServices = Expression.Property(provider, "ResolvedServices");
  17:         var tryGetValue = Expression.Call(resolvedServices, "TryGetValue", null, service, instance);
  18:         var index = Expression.MakeIndex(resolvedServices, typeof(ConcurrentDictionary<IService, object>).GetProperty("Item"), new Expression[] { service});
  19:         var assign = Expression.Assign(index, this.ServiceCallSite.Build(provider));
  20:  
  21:         return Expression.Block(typeof(object),new[] { instance },Expression.IfThen(Expression.Not(tryGetValue),assign),index);
  22:     }
  23:  
  24:     public virtual object Invoke(ServiceProvider provider)
  25:     {
  26:         object instance;
  27:         return provider.ResolvedServices.TryGetValue(this.Service, out instance)
  28:             ? instance
  29:             : provider.ResolvedServices[this.Service] = this.ServiceCallSite.Invoke(provider);
  30:     }
  31: }

其實Singleton和Scope這兩種模式本質上是等同的,它們表示在某個ServiceScope中的“單例”模式,它們之間的不同之處在于前者的ServiceScope是針對作為根的ServiceProvider,后者則是針對當前ServiceProvider。所以SingletonCallSite是ScopedCallSite的派生類,具體定義如下所示。

   1: internal class SingletonCallSite : ScopedCallSite
   2: {
   3:     public SingletonCallSite(IService service, IServiceCallSite serviceCallSite) : 
   4:     base(service, serviceCallSite)
   5:     { }
   6:  
   7:     public override Expression Build(Expression provider)
   8:     {
   9:         return base.Build(Expression.Property(provider, "Root"));
  10:     }
  11:  
  12:     public override object Invoke(ServiceProvider provider)
  13:     {
  14:         return base.Invoke(provider.Root);
  15:     }
  16: }


三、創建ServiceCallSite

ServiceCallSite的創建體現在IService接口的CreateServiceSite方法中,在我們的Service類這個方法是如何實現的呢?如下面的代碼片段所示,真正用于創建服務實例的ServiceCallSite先根據當前的ServiceDescriptor創建出來,然后再根據采用生命周期管理模式創建出與之匹配的ServiceCallSite。

   1: internal class Service : IService
   2: {
   3:     public IServiceCallSite CreateCallSite(ServiceProvider provider, ISet<Type> callSiteChain)
   4:     {
   5:         IServiceCallSite serviceCallSite = 
   6:             (null != this.ServiceDescriptor.ImplementationInstance)
   7:             ? new InstanceCallSite(this.ServiceDescriptor.ImplementationInstance)
   8:             : null;
   9:  
  10:         serviceCallSite = serviceCallSite?? 
  11:             ((null != this.ServiceDescriptor.ImplementationFactory)
  12:             ? new FactoryCallSite(this.ServiceDescriptor.ImplementationFactory)
  13:             : null);
  14:  
  15:         serviceCallSite = serviceCallSite ?? this.CreateConstructorCallSite(provider, callSiteChain);
  16:  
  17:         switch (this.Lifetime)
  18:         {
  19:             case ServiceLifetime.Transient: return new TransientCallSite(serviceCallSite);
  20:             case ServiceLifetime.Scoped: return new ScopedCallSite(this, serviceCallSite);
  21:             default: return new SingletonCallSite(this, serviceCallSite);
  22:         }
  23:     }
  24:     //其他成員    
  25: }

ASP.NET Core中的依賴注入(1):控制反轉(IoC)
ASP.NET Core中的依賴注入(2):依賴注入(DI)
ASP.NET Core中的依賴注入(3):服務注冊與提取
ASP.NET Core中的依賴注入(4):構造函數的選擇與生命周期管理
ASP.NET Core中的依賴注入(5):ServicePrvider實現揭秘【總體設計】
ASP.NET Core中的依賴注入(5):ServicePrvider實現揭秘【解讀ServiceCallSite】
ASP.NET Core中的依賴注入(5):ServicePrvider實現揭秘【補充漏掉的細節】


文章列表


不含病毒。www.avast.com
arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()