之前的 如何取得 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 依賴移除改為強行別的 MySettingspublic 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 的 instancepublic 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 :)

網誌管理員已經移除這則留言。
回覆刪除