文章出處

我們在《讀取配置信息》通過實例的形式演示了如何利用Options模型以依賴注入的方式直接獲取由指定配置節綁定生成的Options對象,我們再次回顧一下當初我們編寫的程序。如下面的代碼片段所示,基于Options模型的配置綁定的編程基本采用這樣的模式:先后調用ServiceCollection的擴展方法AddOption和Configure注冊Options模型相關的服務并完成Options類型與指定配置節之間的映射,然后利用由此生成ServiceProvider獲得一個類型為IOptions<TOptions>的服務示例,后者的Value就是配置綁定生成的Options對象。

   1: FormatSettings settings = new ServiceCollection()
   2:     .AddOptions()
   3:     .Configure<FormatSettings>(configuration)
   4:     .BuildServiceProvider()
   5:     .GetService<IOptions<FormatSettings>>()
   6:     .Value;


一、IOptions <TOptions>

由于Options模型的編程僅僅涉及到上述幾個方法的調用,所以只要搞清楚這幾個方法背后的實現邏輯,我們也就徹底了解了Options模型的實現原理。首先當我們調用ServiceCollection的擴展方法時,實際上僅僅是按照如下的方式注冊了一個針對IOptions <TOptions>接口類型的服務而已。服務接口IOptions<TOptions>僅僅定義了一個只讀屬性Value,該屬性返回的正是綁定了指定配置數據的Options對象。

   1: public static class ServiceCollectionExtensions
   2: {
   3:     public static IServiceCollection AddOptions(this IServiceCollection services)
   4:     {
   5:         return services.AddSingleton(typeof(IOptions<>), typeof(OptionsManager<>));
   6:     }
   7: }
   8:  
   9: public interface IOptions<TOptions> where TOptions:class, new()
  10: {
  11:     TOptions Value { get; }
  12: }

通過上面的給出的代碼片段我們不難看出,AddOptions方法實際上是以Singleton模式注冊了一個類型為OptionsManager<TOptions>的服務,如下所示的代碼片段基本反映了該類型的實現邏輯。如下面的代碼片段所示,OptionsManager<TOptions>的只讀屬性Value返回的Options對象是以“延遲加載(Lazy Loading)”的形式被提供。Options對象創建的邏輯也很簡單,我們直接調用其默認構造函數創建一個空的Options對象,然后將其遞交給在構造函數中指定的一系列IConfigureOptions<TOptions>進行設置,配置綁定就這這個過程中完成。

   1: public class OptionsManager<TOptions> : IOptions<TOptions> where TOptions : class, new()
   2: {
   3:     private Lazy<TOptions> optionsAccessor;
   4:     public OptionsManager(IEnumerable<IConfigureOptions<TOptions>> setups)
   5:     {
   6:         optionsAccessor = new Lazy<TOptions>(() =>
   7:         {
   8:             TOptions options = new TOptions();
   9:             setups.ForEach(it => it.Configure(options));
  10:             return options;
  11:         });
  12:     }
  13:     public TOptions Value
  14:     {
  15:         get { return optionsAccessor.Value; }
  16:     }
  17: }


二、IConfigureOptions<TOptions>

IConfigureOptions<TOptions>接口抽象了針對Options對象的配置行為,這個行為體現在定義其中的Configure方法。ConfigureOptions<TOptions>實現了這個接口,它采用在構造函數提供的Action<TOptions>完成對Options對象的配置。

   1: public interface IConfigureOptions<TOptions> where TOptions : class, new()
   2: {
   3:     void Configure(TOptions options);
   4: }
   5:  
   6: public class ConfigureOptions<TOptions> : IConfigureOptions<TOptions> where TOptions : class, new()
   7: {
   8:     public Action<TOptions> Action { get; private set; }
   9:     public ConfigureOptions(Action<TOptions> action)
  10:     {
  11:         this.Action = action;
  12:     }
  13:     public void Configure(TOptions options)
  14:     {
  15:         this.Action(options);
  16:     }
  17: }

針對Options對象的配置綁定工作實現在一個名為ConfigureFromConfigurationOptions<TOptions>的類中。如下面的代碼片段所示,這個類型直接繼承ConfigureOptions<TOptions>,在構造函數中指定的Configuration對象承載了最終需要綁定到Options對象上的配置數據,它直接調用Configuration對象的擴展方法Bind完成了針對Options對象的配置綁定。

   1: public class ConfigureFromConfigurationOptions<TOptions>: ConfigureOptions<TOptions> where TOptions : class, new()
   2: {
   3:     public ConfigureFromConfigurationOptions(IConfiguration configuration) : base(options => configuration.Bind(options))
   4:     { }
   5: }

在Options模型中,ConfigureFromConfigurationOptions<TOptions>對象通過擴展方法Configure方法被注冊到指定的ServiceCollection對象中。如下面的代碼片段所示,Configure方法直接利用作為參數傳入的Configuration對象創建一個ConfigureFromConfigurationOptions<TOptions>對象,并將這個對象注冊到ServiceCollection之中。

   1: public static class ServiceCollectionExtensions
   2: {
   3:     public static IServiceCollection Configure<TOptions>(this IServiceCollection services, IConfiguration configuration) 
   4:     where TOptions : class, new()
   5:     {
   6:         return services.AddInstance<IConfigureOptions<TOptions>>(new ConfigureFromConfigurationOptions<TOptions>(configuration));
   7:     }
   8: }


三、Options對象的提供

9整個Options模型以兩個注冊到ServiceCollection的服務為核心,這兩個服務對應的服務接口分別是IOptions <TOptions>和IConfigureOptions<TOptions>,前者直接提供最終綁定了配置數據的Options對象,后者則在Options對象返回之前對它實施相應的初始化工作。這個兩個服務分別通過擴展方法AddOptions和Configure方法注冊到指定的ServiceCollection之中,服務的真實類型分別是OptionsManager<TOptions>和ConfigureFromConfigurationOptions<TOptions>,后者派生于ConfigureOptions<TOptions>。右圖所示的UML體現了Options模型中涉及的這些接口和類型之間的關系。

對于一個包含服務注冊描述信息的ServiceCollection,當我們分別調用其擴展方法AddOptions和Configure完成了相應的服務注冊之后,我們就可以利用由它生成的ServiceProvider對象來提供針對接口類型IOptions <TOptions>的服務實例,并通過后者的只讀屬性Value得到配置綁定生成的Options對象。

ServiceProvider提供的這個服務實例自然是一個OptionsManager<TOptions>對象,當ServiceProvider調用構造函數對它進行實例化的時候,我們注冊的ConfigureFromConfigurationOptions<TOptions>對象會以構造器注入的形式作為參數。在構造函數執行過程中,一個空的Options對象先被創建出來后會作為參數調用ConfigureFromConfigurationOptions<TOptions>的Configure方法,后者將在預先指定的Configuration對象綁定到這個Options對象之上。

 

ASP.NET Core的配置(1):讀取配置信息
ASP.NET Core的配置(2):配置模型詳解
ASP.NET Core的配置(3): 將配置綁定為對象[上篇]
ASP.NET Core的配置(3): 將配置綁定為對象[下篇]
ASP.NET Core的配置(4):多樣性的配置源[上篇]
ASP.NET Core的配置(4):多樣性的配置源[中篇]
ASP.NET Core的配置(4):多樣性的配置源[下篇]
ASP.NET Core的配置(5):配置的同步[上篇]
ASP.NET Core的配置(5):配置的同步[下篇]


文章列表


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

    IT工程師數位筆記本

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