ASP.NET Core中Options模式

ASP.NET Core Options模式可以使用强类型的类来访问一组关联的配置,其实Options模式的目的实现了软件工程中2个基本的原则

  • 仅仅依赖于他们自己使用的配置

  • 关注点分离

同时Options模式也提供了一种数据验证的机制

1. 绑定分层配置

我们在项目文件的appsettings.json文件中新增如下内容:

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

接着在项目文件夹下创建PositionOptions类:

ba7b1c774da60724719a2382f2fe3f6a.png

这个类需要注意以下几点:

  • 该类必须是非抽象类

  • 属性的类型必须与配置中的类型相符合,例如:配置中是string类型,你的属性也必须是string类型,而不能是int类型

  • 属性的名称必须与配置中Key的名称保持一致

  • 不能绑定字段

1.1 使用Bind方法

应用程序启动之后,如果修改配置文件这个方法是会自动读取最新的配置文件

6d30e059f4292fb3c74e92cc37ae499a.png

1.2 使用Get<T>方法

应用程序启动之后,如果修改配置文件这个方法是会自动读取最新的配置文件

a46fd43dcda6e45b4051be88453c6bdb.png

2. Options模式

2.1 IOptions<TOptions>接口

  • Singleton生命周期

  • 不能在Singleton的生命周期的服务内使用

  • 仅仅读取一次

  • 应用程序启动之后,如果配置发生变更不会通知

我们可以使用Options模式替换上面方法,在启动类中添加如下方法,下面代码添加PositionOptions到容器:

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

有了上面的配置我们可以使用下面代码读取该配置:

using AspNetCore.OptionsPattern.Models;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
namespace AspNetCore.OptionsPattern.Controllers
{
    public class IOptionsInterfaceController : Controller
    {
        private IOptions<PositionOptions> _positionOptions;


        public IOptionsInterfaceController(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;
namespace AspNetCore.OptionsPattern.Controllers
{
    public class IOptionsSnapshotInterfaceController : Controller
    {
        private IOptionsSnapshot<PositionOptions> _optionsSnapshot;
        public IOptionsSnapshotInterfaceController(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.GetSection(MyConfigOptions.MyConfig))
            .ValidateDataAnnotations();

调用AddOptions方法来获取一个OptionsBuilder<TOptions>对象,绑定到MyConfigOptions类,ValidateDataAnnotations方法来启用DataAnnotations的验证

我们还可以做一些复杂的验证逻辑,如下代码所示:

builder.Services.AddOptions<MyConfigOptions>()
            .Bind(builder.Configuration.GetSection(MyConfigOptions.MyConfig))
            .ValidateDataAnnotations()
            .Validate(config =>
            {
                if (config.Key2 != 0)
                {
                    return config.Key3 > config.Key2;
                }


                return true;
            }, "Key3 must be > than Key2.");

我们也可以将验证逻辑放到单独的类中来实现,我们看一下Validate方法的源代码:

2877257a2d7df11ab8864f5a63d7768b.png

1f5205c511250a1411cbb5231863f555.png

我们也可以自定义一个类来实现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.GetSection(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.GetSection(MyConfigOptions.MyConfig));


builder.Services.AddSingleton<IValidateOptions
                              <MyConfigOptions>, MyConfigValidation>();

源代码地址:

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值