之前的 如何取得 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 屬性
"MySettings": { "Name": "Marcus", "Title": "9527" }接著要取得設定檔的內容,這邊建立與 Config 內容欄位相同的強型別的 class 物件
public class MySettings { public string Name { get; set; } public string Title { get; set; } }在 ConfigureServices 中加入下列代碼,用意是將 appsettinss.json 中的資訊加載到 MySettings 中
public void ConfigureServices(IServiceCollection services) { services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2); services.AddOptions(); services.Configure<MySettings>(Configuration.GetSection("MySettings")); }接著就可以在 Value Controll 使用 IOptions<T> 取得設定檔的資訊,代碼如下
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 加入擴充方法,代碼如下
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; } }這裡我們在 startup.cs 時手動使用 Microsoft.Extensions.Configuration.Binder 來綁定設定檔,並指定服務容器生命週期 (LifeTime) 為 Singleton,在 ASP.NET Core DI 預設提供的 Lifetime 有下列三種
- Transient : 每次請求時都會產生新的 Instance
- Scoped : 每個 http Request 都會產生一份 Instance
- Singleton : 整個 Application 只會有一份 Instance
新增完擴充方法之後,接著下一步就是在 ConfigureServices 改用新增的擴充方法 confugurePOCO
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")); }接著在回到要使用的地方也就是範例的 ValueController, 將 IOptions 依賴移除改為強行別的 MySettings
public MySettings _mySettings { get; set; } public ValuesController(MySettings settings) { _mySettings = settings; }在重新執行應用程式,可以發現應用程式執行正常無誤
如果你對於擴充方法很熟悉,也可以針對自己的需求來自訂所需的方法簽章,例如新增一個 TConfig 參數做為繫結設定檔,代碼如下
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;
}
}
使用方式先 new 之後作為參數傳入public void ConfigureServices(IServiceCollection services) { services.AddMvc(); var mySettings = new MySettings("foo"); services.ConfigurePOCO(Configuration.GetSection("MySettings"), mySettings); }或者是新增 Func<TConfig> 參數透過委派方法新增 TConfig 的 instance
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;
}
}
使用方式如下public void ConfigureServices(IServiceCollection services) { //... services.ConfigurePOCO(Configuration.GetSection("MySettings"), () => new MySettings("foo")); //... }如果想了解更多細節,可以參考 Strongly typed configuration in ASP.NET Core without IOptions<T> 取得更多資訊,希望透過以上的介紹,可以讓跟我有一樣困擾的開發者得到新的作法,Hope it helps :)
網誌管理員已經移除這則留言。
回覆刪除