之前的 如何取得 appsettings.json 組態設定 文章中有介紹在 ASP.NET Core 中透過 IOptions 方法取得設定檔的方法,在需要用到的地方注入 IOptions 取得設定類別的資訊,相信使用上並不困難在 MSDN 官方推薦作法也是如此,但如果開發一陣子之後可以發現到處都是 IOptios 類別,這篇文章介紹如何使用擴充 IServiceCollection 的方法來降低對 IOptios 的依賴,若有問題或是錯誤的地方歡迎各方高手大大一起討論或給予指導。
IOption
之首先先來簡單回顧 IOptions<T> 的傳統用法,透過微軟 MSDN 中 IOptions 介紹得知要使用前需先引用 Microsoft.Extensions.Options,為了方便快速了解差異性,這裡建立一個 ASP.NET Core Web 應用程式範例來說明,在新增完應用程式後在 appsetting.json 加入自己定義的 mySettings 設定資訊提供 Name 以及 Title 屬性
接著要取得設定檔的內容,這邊建立與 Config 內容欄位相同的強型別的 class 物件
- "MySettings": {
- "Name": "Marcus",
- "Title": "9527"
- }
在 ConfigureServices 中加入下列代碼,用意是將 appsettinss.json 中的資訊加載到 MySettings 中
- public class MySettings
- {
- public string Name { get; set; }
- public string Title { get; set; }
- }
接著就可以在 Value Controll 使用 IOptions<T> 取得設定檔的資訊,代碼如下
- public void ConfigureServices(IServiceCollection services)
- {
- services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
- services.AddOptions();
- services.Configure<MySettings>(Configuration.GetSection("MySettings"));
- }
- public class ValuesController : ControllerBase
- {
- public MySettings _mySettings { get; set; }
- public ValuesController(IOptions<MySettings> settings)
- {
- _mySettings = settings.Value;
- }
- // GET api/values
- [HttpGet]
- public ActionResult<IEnumerable<string>> Get()
- {
- return new string[] { _mySettings.Name, _mySettings.Title };
- }
不使用 IOptions 注入
以上快速地回顧 IOptions<T> 的用法,就可以簡單使用 IOption<T> 取得 appsettings.json 設定資訊的方法,但是這意味著你的代碼與 IOptions 有著強制依賴的關係,你有多少 Controller 就需要在各別的 Controller 中都 using 所需要的 Microsoft.Extensions.Options,除非 appsettings.json 中的設定很常異動需要進行重新載入( 這時可以使用 IOptionMonitor 而不是 IOptions ),否則大部分的使用情境中 config 設定都是較少異動的,參考此文章之後有了新的解法,我們可以新增一個類別並針對 IServiceCollection 加入擴充方法,代碼如下
這裡我們在 startup.cs 時手動使用 Microsoft.Extensions.Configuration.Binder 來綁定設定檔,並指定服務容器生命週期 (LifeTime) 為 Singleton,在 ASP.NET Core DI 預設提供的 Lifetime 有下列三種
- public static class ServiceCollectionExtensions
- {
- public static TConfig ConfigurePOCO<TConfig>(this IServiceCollection services, IConfiguration configuration) where TConfig : class, new()
- {
- if (services == null) throw new ArgumentNullException(nameof(services));
- if (configuration == null) throw new ArgumentNullException(nameof(configuration));
- var config = new TConfig();
- configuration.Bind(config);
- services.AddSingleton(config);
- return config;
- }
- }
- Transient : 每次請求時都會產生新的 Instance
- Scoped : 每個 http Request 都會產生一份 Instance
- Singleton : 整個 Application 只會有一份 Instance
新增完擴充方法之後,接著下一步就是在 ConfigureServices 改用新增的擴充方法 confugurePOCO
接著在回到要使用的地方也就是範例的 ValueController, 將 IOptions 依賴移除改為強行別的 MySettings
- public void ConfigureServices(IServiceCollection services)
- {
- services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
- services.AddOptions();
- services.ConfigurePOCO<MySettings>(Configuration.GetSection("MySettings"));
- //services.Configure<MySettings>(Configuration.GetSection("MySettings"));
- }
在重新執行應用程式,可以發現應用程式執行正常無誤
- public MySettings _mySettings { get; set; }
- public ValuesController(MySettings settings)
- {
- _mySettings = settings;
- }
如果你對於擴充方法很熟悉,也可以針對自己的需求來自訂所需的方法簽章,例如新增一個 TConfig 參數做為繫結設定檔,代碼如下
使用方式先 new 之後作為參數傳入
- public static class ServiceCollectionExtensions
- {
- public static TConfig ConfigurePOCO<TConfig>(this IServiceCollection services, IConfiguration configuration, TConfig config) where TConfig : class
- {
- if (services == null) throw new ArgumentNullException(nameof(services));
- if (configuration == null) throw new ArgumentNullException(nameof(configuration));
- if (config == null) throw new ArgumentNullException(nameof(config));
- configuration.Bind(config);
- services.AddSingleton(config);
- return config;
- }
- }
或者是新增 Func<TConfig> 參數透過委派方法新增 TConfig 的 instance
- public void ConfigureServices(IServiceCollection services)
- {
- services.AddMvc();
- var mySettings = new MySettings("foo");
- services.ConfigurePOCO(Configuration.GetSection("MySettings"), mySettings);
- }
使用方式如下
- public static class ServiceCollectionExtensions
- {
- public static TConfig ConfigurePOCO<TConfig>(this IServiceCollection services, IConfiguration configuration, Func<TConfig> pocoProvider) where TConfig : class
- {
- if (services == null) throw new ArgumentNullException(nameof(services));
- if (configuration == null) throw new ArgumentNullException(nameof(configuration));
- if (pocoProvider == null) throw new ArgumentNullException(nameof(pocoProvider));
- var config = pocoProvider();
- configuration.Bind(config);
- services.AddSingleton(config);
- return config;
- }
- }
如果想了解更多細節,可以參考 Strongly typed configuration in ASP.NET Core without IOptions<T> 取得更多資訊,希望透過以上的介紹,可以讓跟我有一樣困擾的開發者得到新的作法,Hope it helps :)
- public void ConfigureServices(IServiceCollection services)
- {
- //...
- services.ConfigurePOCO(Configuration.GetSection("MySettings"), () => new MySettings("foo"));
- //...
- }
網誌管理員已經移除這則留言。
回覆刪除