當前位置: 妍妍網 > 碼農

ASP.NET Core中Options模式

2024-02-27碼農

ASP.NET Core Options模式可以使用強型別的類來存取一組關聯的配置 ,其實Options模式的目的實作了軟體工程中2個基本的原則

  • 僅僅依賴於他們自己使用的配置

  • 關註點分離

  • 同時Options模式也提供了一種數據驗證的機制

    1. 繫結分層配置

    我們在計畫檔的appsettings.json檔中新增如下內容:

    "Position": { "Title": "Editor", "Name": "Joe Smith" }

    接著在計畫資料夾下建立 PositionOptions 類:

    這個類需要註意以下幾點:

  • 該類必須是非抽象類

  • 內容的型別必須與配置中的型別相符合,例如:配置中是string型別,你的內容也必須是string型別,而不能是int型別

  • 內容的名稱必須與配置中Key的名稱保持一致

  • 不能繫結欄位

  • 1.1 使用Bind方法

    應用程式啟動之後,如果修改配置檔這個方法是會自動讀取最新的配置檔

    1.2 使用Get<T>方法

    應用程式啟動之後,如果修改配置檔這個方法是會自動讀取最新的配置檔

    2. Options模式

    2.1 IOptions <TOptions>介面

  • Singleton生命周期

  • 不能在Singleton的生命周期的服務內使用

  • 僅僅讀取一次

  • 應用程式啟動之後,如果配置發生變更不會通知

  • 我們可以使用Options模式替換上面方法,在啟動類中添加如下方法,下面程式碼添加PositionOptions到容器:

    builder.Services.Configure<PositionOptions>(builder.Configuration.Getp(PositionOptions.Position));

    有了上面的配置我們可以使用下面程式碼讀取該配置:

    using AspNetCore.OptionsPattern.Models;using Microsoft.AspNetCore.Mvc;using Microsoft.Extensions.Options;namespaceAspNetCore.OptionsPattern.Controllers{public classIOptionsInterfaceController : Controller {private IOptions<PositionOptions> _positionOptions;publicIOptionsInterfaceController(IOptions<PositionOptions> options) { _positionOptions = options; }public IActionResult Index() {return View(_positionOptions.Value); } }}

    2.2 IOptionsSnapshot<TOptions>介面

  • Scoped生命周期

  • 不能在Singleton的生命周期的服務內使用

  • 每個Scoped讀取數據

  • 配置的數據量如果特別大,效能非常差(https://github.com/dotnet/runtime/issues/53793)

  • using AspNetCore.OptionsPattern.Models;using Microsoft.AspNetCore.Mvc;using Microsoft.Extensions.Options;namespaceAspNetCore.OptionsPattern.Controllers{public classIOptionsSnapshotInterfaceController : Controller {private IOptionsSnapshot<PositionOptions> _optionsSnapshot;publicIOptionsSnapshotInterfaceController(IOptionsSnapshot<PositionOptions> optionsSnapshot) { _optionsSnapshot = optionsSnapshot; }public IActionResult Index() {return View(_optionsSnapshot.Value); } }}

    2.3 IOptionsMonitor<TOptions>介面

  • Singleton生命周期

  • 能夠在任何的生命周期的服務內使用

  • 即時讀取數據

  • 通常在後台的服務中使用

  • 變更通知(當配置發生變化的時候,會自動通知)

  • using AspNetCore.OptionsPattern.Models;using Microsoft.AspNetCore.Mvc;using Microsoft.Extensions.Options;namespace AspNetCore.OptionsPattern.Controllers{ public class IOptionsMonitorInterfaceController : Controller { private IOptionsMonitor<PositionOptions> _positionOptions; public IOptionsMonitorInterfaceController(IOptionsMonitor<PositionOptions> optionsMonitor) { _positionOptions = optionsMonitor; } public IActionResult Index() { return View(_positionOptions.CurrentValue); } }}

    3. Options數據驗證

    我們可以在繫結過程中對相應的值進行驗證,將下面配置添加到appsettings.json檔中:

    "MyConfig": { "Key1": "My Key One", "Key2": 10, "Key3": 32 }

    我們在定義一個類來繫結上面節點:

    namespace AspNetCore.OptionsPattern.Models{ public class MyConfigOptions { public const string MyConfig = "MyConfig"; [RegularExpression(@"^[a-zA-Z''-'\s]{1,40}$")] public string Key1 { get; set; } [Range(0, 1000, ErrorMessage = "Value for {0} must be between {1} and {2}.")] public int Key2 { get; set; } public int Key3 { get; set; } }}

    在啟動類中添加如下程式碼:

    builder.Services.AddOptions<MyConfigOptions>() .Bind(builder.Configuration.Getp(MyConfigOptions.MyConfig)) .ValidateDataAnnotations();

    呼叫AddOptions方法來獲取一個OptionsBuilder<TOptions>物件,繫結到MyConfigOptions類, ValidateDataAnnotations方法來啟用DataAnnotations的驗證

    我們還可以做一些復雜的驗證邏輯,如下程式碼所示:

    builder.Services.AddOptions<MyConfigOptions>() .Bind(builder.Configuration.Getp(MyConfigOptions.MyConfig)) .ValidateDataAnnotations() .Validate(config => { if (config.Key2 != 0) { return config.Key3 > config.Key2; } return true; }, "Key3 must be > than Key2."); 

    我們也可以將驗證邏輯放到單獨的類中來實作,我們看一下Validate方法的原始碼:

    我們也可以自訂一個類來實作IValidateOptions<TOptions>實作我們的驗證邏輯,接下來我們自訂一個驗證邏輯類:

    using Microsoft.Extensions.Options;using System.Text.RegularExpressions;namespace AspNetCore.OptionsPattern.Models{ public class MyConfigValidation : IValidateOptions<MyConfigOptions> { public MyConfigOptions _config { get; private set; } public MyConfigValidation(IConfiguration config){ _config = config.Getp(MyConfigOptions.MyConfig) .Get<MyConfigOptions>(); } public ValidateOptionsResult Validate(string name, MyConfigOptions options){ string? vor = null; var rx = new Regex(@"^[a-zA-Z''-'\s]{1,40}$"); var match = rx.Match(options.Key1!); if (string.IsNullOrEmpty(match.Value)) { vor = $"{options.Key1} doesn't match RegEx \n"; } if (options.Key2 < 0 || options.Key2 > 1000) { vor = $"{options.Key2} doesn't match Range 0 - 1000 \n"; } if (_config.Key2 != default) { if (_config.Key3 <= _config.Key2) { vor += "Key3 must be > than Key2."; } } if (vor != null) { return ValidateOptionsResult.Fail(vor); } return ValidateOptionsResult.Success; } }}

    修改啟動類中的配置:

    builder.Services.AddOptions<MyConfigOptions>() .Bind(builder.Configuration.Getp(MyConfigOptions.MyConfig));builder.Services.AddSingleton<IValidateOptions <MyConfigOptions>, MyConfigValidation>();

    原始碼地址:

    https://github.com/bingbing-gui/Asp.Net-Core-Skill/tree/master/Fundamentals/AspNetCore.Options/AspNetCore.OptionsPattern