.NET Core采用的全新配置係統[3]: “Options模式”下的配置是如何綁定為Options對象
配置的原子結構就是單純的鍵值對,並且鍵和值都是字符串,但是在真正的項目開發中我們一般不會單純地以鍵值對的形式來使用配置。值得推薦的做法就是采用《.NET Core采用的全新配置係統[1]: 讀取配置數據》最後演示的方式,這樣就能利用它們之間的映射關係將讀取的配置數據綁定為Options對象,我們將這種編程模式稱為“”。 [ 本文已經同步到《ASP.NET Core框架揭秘》之中]
目錄
一、配置綁定
二、擴展方法AddOptions
三、擴展方法Configure
四、Options對象的創建
對於一個Options對象來說,如果我們將其數據成員(這裏主要指屬性成員)視為其子節點,那麼一個Options對象同樣具有樹形層次化結構,這與通過Configuration對象表示的配置樹在結構上並沒有本質的區別。如果Options類型的數據成員定義與配置樹結構具有匹配的結構,那麼將後者綁定為一個對應類型的Options對象是一件很容易的事情,對於這種將一個Configuration對象綁定為對應Options對象的行為簡稱為“”。
配置綁定讓我們可以根據得到的Configuration對象生成相應的Options對象,相關的API定義在“Microsoft.Extensions.Configuration.Binder”這個NuGet包中,後者為IConfiguration接口定義了如下一個GetValue方法得到綁定生成的Options對象。在調用這個放過的時候,我們會創建一個空的Options對象並將其作為參數,該方法會將Configuration承載的配置數據綁定到Options對象上。
1: public static class ConfigurationBinder
2: {
3: public static void Bind(this IConfiguration configuration, object instance);
4: }
配置綁定的目標類型可以是一個簡單的,也可以是一個,還可以是一個、或者類型。上述的這個Bind方法在進行配置綁定的過程,針對不同的目標類型,它會采用不同的策略。至於該方法具體的實現原理,我們會在後續的部分予以單獨介紹,而目前介紹的重點是Options模式采用的API在背後是如何調用這個方法得到所需的Options對象的。
我們在回顧一下《.NET Core采用的全新配置係統[1]: 讀取配置數據》演示的采用Options模式讀取配置的例子。Options模式是對依賴注入的應用,我們知道針對依賴注入的編程隻涉及兩個方麵,即注冊相應的服務到ServiceCollection對象上,在利用後者創建相應的ServiceProvider來提供我們所需的服務對象。如下麵的代碼片段所示,Options模式最終的目的是利用ServiceProvider得到一個類型為IOptions<TOptions>的服務對象,後者的Value通過配置綁定生成的Options對象。為了能夠得到所需的服務對象,它借助兩個擴展方法AddOptions和Configure<TOptions>注冊了必要的服務。
1: IConfiguration config = ...;
2: FormatOptions options = new ServiceCollection()
3: .AddOptions()
4: .Configure<FormatOptions>(config.GetSection("Format"))
5: .BuildServiceProvider()
6: .GetService<IOptions<FormatOptions>>()
7: .Value;
依然Options對象最終是利用依賴注入的方式創建的一個類型為IOptions<TOptions>的服務對象得到的,我們就先來認識一下這個接口。這是一個泛型接口,泛型參數類型TOptions代碼的正式Options對象對應的類型。IOptions<TOptions>接口的定義如下,它隻有一個唯一的隻讀屬性Value返回我們所需的Options對象。
1: public interface IOptions<out TOptions> where TOptions: class, new()
2: {
3: TOptions Value { get; }
4: }
當我們調用ServiceCollection的AddOptions的時候,該方法僅僅是按照如下的方式針對該類型注冊了一個服務而已,這個服務的真實類型為OptionsManager <TOptions> ,注冊的服務采用的生命周期模式為Singleton。換句話說,配置綁定生成的Options對象最終返回的實際上是通過OptionsManager <TOptions> 創建的。
1: public static IServiceCollection AddOptions(this IServiceCollection services)
2: {
3: services.TryAdd(ServiceDescriptor.Singleton(typeof(IOptions<>), typeof(OptionsManager<>)));
4: return services;
5: }
如下所示的是 OptionsManager <TOptions> 類型的定義,我們可以看到它的構造函數接受一個元素類型為IConfigureOptions<TOptions>的集合作為參數,我們將實現了該接口的類型以及對應對象統稱為ConfigureOptions<TOptions>。IConfigureOptions<TOptions>接口定義了一個唯一的Configure方法,該方法將一個Options對象作為輸入參數。從定義可以看出一個ConfigureOptions<TOptions>對象的作用與一個類型為Action<TOptions>的委托對象,所以對於它的實現類型ConfigureOptions<TOptions>來說,對應的對象就直接通過一個Action<TOptions>對象來創建。
1: public class OptionsManager<TOptions> : IOptions<TOptions> where TOptions: class, new()
2: {
3: public OptionsManager(IEnumerable<IConfigureOptions<TOptions>> setups);
4: public virtual TOptions Value { get; }
5: }
6:
7: public interface IConfigureOptions<in TOptions> where TOptions: class
8: {
9: void Configure(TOptions options);
10: }
11:
12: public class ConfigureOptions<TOptions>: IConfigureOptions<TOptions> where TOptions : class, new()
13: {
14: public Action<TOptions> Action { get; private set; }
15: public ConfigureOptions(Action<TOptions> action)
16: {
17: this.Action = action;
18: }
19: public void Configure(TOptions options)
20: {
21: this.Action(options);
22: }
23: }
Options對象的創建體現在 OptionsManager <TOptions>類型的Value屬性上。該屬性的實現非常簡單,它先調用默認無參構造函數(Options類型必須具有一個默認無參構造函數)創建一個空的Options對象,在返回之前,它會將其遞交給初始化時指定的ConfigureOptions<TOptions>對象進行逐個處理。毫無疑問,針對Bind方法的調用肯定是通過某個ConfigureOptions<TOptions>對象參與到整個流程之中的,具體的實現自然與另一個擴展方法Configure有關。
Options模式僅僅涉及到針對ServiceCollection的兩個擴展方法(AddOptions和Configure<TOptions>),前者將服務IOptions<TOptions>/ OptionsManager <TOptions>注冊到ServiceCollection之上,後者又作了怎樣的服務注冊呢?
1: public static IServiceCollection Configure<TOptions>(this IServiceCollection services, IConfiguration config) where TOptions: class
2: {
3: return services.AddSingleton<IConfigureOptions<TOptions>>( new ConfigureFromConfigurationOptions<TOptions>(config));
4: }
5:
6: public class ConfigureFromConfigurationOptions<TOptions> :ConfigureOptions<TOptions> where TOptions : class
7: {
8: public ConfigureFromConfigurationOptions(IConfiguration config)
9: : base(options => config.Bind(options))
10: { }
11: }
從上麵的代碼片段可以看出,當我們調用ServiceCollection的擴展方法Configure<TOptions>時,該方法會利用指定 的Configuration對象創建一個ConfigureFromConfigurationOptions對象,並以服務類型IConfigureOptions<TOptions>注冊到ServiceCollection上,采用的生命周期模式為Singleton。至於類型ConfigureFromConfigurationOptions,它是上麵介紹的ConfigureOptions<TOptions>類型的繼承者,創建該對象指定的Action<TOptions>委托對象通過調用Configuration對象的擴展方法Bind最終實現了配置綁定。
Options編程模式的背後以兩個注冊到ServiceCollection的服務為核心,這兩個服務對應的服務接口分別是IOptions<TOptions>和IConfigureOptions<TOptions>,前者直接提供最終綁定了配置數據的Options對象,後者則在Options對象返回之前對它實施相應的初始化工作。這個兩個服務分別通過擴展方法AddOptions和Configure方法注冊到指定的ServiceCollection之中,服務的真實類型分別是OptionsManager<TOptions>和ConfigureFromConfigurationOptions<TOptions>,後者派生於ConfigureOptions<TOptions>。下圖所示的UML體現了Options模型中涉及的這些接口/類型以及它們之間的關係。
微信公眾賬號:大內老A
微博:www.weibo.com/artech
如果你想及時得到個人撰寫文章以及著作的消息推送,或者想看看個人推薦的技術資料,可以掃描左邊二維碼(或者長按識別二維碼)關注個人公眾號(原來公眾帳號蔣金楠的自媒體將會停用)。
本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁麵明顯位置給出原文連接,否則保留追究法律責任的權利。
最後更新:2017-10-25 12:03:48
上一篇:
.NET Core采用的全新配置係統[2]: 配置模型設計詳解
下一篇:
iOS App 安裝包瘦身指南
Android中 ScrollView(ListView)中嵌套ListView時顯示不全的簡便解決方案
阿裏雲推出獨享虛擬主機專用幸運券,好用又不貴
Mybatis-generator的使用
微軟為IE瀏覽器發布緊急安全更新
Xcode5 支持 iOS5 問題解決過程記錄
數字化醫療滲入行業各個分支 器械廠商正趕上這一波熱點
利用新浪微博接口生成漂亮的微博卡片
Apache Storm 官方文檔 —— 理解 Storm 拓撲的並行度(parallelism)概念
國內專用,一鍵部署Kubernetes到CentOS 6/7
《軟件工藝師:專業、務實、自豪》一3.7.1 軟件工藝峰會