Options选项

这篇具有很好参考价值的文章主要介绍了Options选项。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

选项用来提供对相关设置的强类型访问,读取配置首选使用选项模式。选项无法脱离容器使用,依赖容器,实现了选项不同的访问方式。选项模式使用了泛型包装器,因此具备了如下优点:

  • 不需要显示注册选项具体类型,只需要将泛型包装器注入到容器中;
  • 对于选项实例的评估推迟到获取IOptions.Value时进行,而不是在注入时进行,这样就可以获取不同生命周期的选项;
  • 可以对选项进行泛型约束;

选项注入

选项模式向容器中注入了三种类型的选项泛型包装器:IOptions<>,IOptionsSnapshot<>,IOptionsMonitor<>。其中IOptionsSnapshot<>被注册为Scoped。注入了IOptionsFactory<>泛型选项工厂,用来创建选项实例。注入了IOptionsMonitorCache<>,由IOptionsMonitor<>用于缓存泛型实例。

public static IServiceCollection AddOptions(this IServiceCollection services)
{
    if (services == null)
        throw new ArgumentNullException(nameof(services));

    services.TryAdd(ServiceDescriptor.Singleton(typeof(IOptions<>), typeof(UnnamedOptionsManager<>)));
    services.TryAdd(ServiceDescriptor.Scoped(typeof(IOptionsSnapshot<>), typeof(OptionsManager<>)));
    services.TryAdd(ServiceDescriptor.Singleton(typeof(IOptionsMonitor<>), typeof(OptionsMonitor<>)));
    services.TryAdd(ServiceDescriptor.Transient(typeof(IOptionsFactory<>), typeof(OptionsFactory<>)));
    services.TryAdd(ServiceDescriptor.Singleton(typeof(IOptionsMonitorCache<>), typeof(OptionsCache<>)));
    return services;
}

选项不同生命周期

不同类型的选项接口功能不同:

  • IOptions<>:实现类为UnnamedOptionsManager,其中TOptions字段保存选项实例(不支持命名选项)。在首次获取Value时,会调用工厂创建选项实例。因为被注册为单例,因此无法识别配置修改。
  • IOptionsSnapshot<>:实现类为OptionsManager,其中OptionsCache字段(私有,非容器获取)保存选项实例(支持命名选项)。被注册为范围,在范围内首次获取Value时,会调用工厂创建选项实例,并将其保存到私有的OptionsCache中,在范围内选项值不变,不同范围内选项值根据获取时配置文件的不同而不同。
  • IOptionsMonitor<>:实现类为OptionsMonitor,其中IOptionsMonitorCache字段,该字段的值是从容器中解析的,用来缓存选项实例。OptionsMonitor还注入了IOptionsChangeTokenSource列表,可以监听配置源的修改。当监听到修改时,调用工厂重新创建选项以刷新选项值。
internal sealed class UnnamedOptionsManager<[DynamicallyAccessedMembers(
    Options.DynamicallyAccessedMembers)] TOptions> : IOptions<TOptions>
    where TOptions : class
{
    private readonly IOptionsFactory<TOptions> _factory;
    private volatile object _syncObj;
    private volatile TOptions _value;

    public UnnamedOptionsManager(IOptionsFactory<TOptions> factory) => _factory = factory;

    public TOptions Value
    {
        get
        {
            if (_value is TOptions value)
                return value;

            lock (_syncObj ?? Interlocked.CompareExchange(ref _syncObj, new object(), null) ?? _syncObj)
            {
                return _value ??= _factory.Create(Options.DefaultName);
            }
        }
    }
}
public class OptionsManager<[DynamicallyAccessedMembers(Options.DynamicallyAccessedMembers)] TOptions> 
    : IOptions<TOptions>, IOptionsSnapshot<TOptions>
    where TOptions : class
{
    private readonly IOptionsFactory<TOptions> _factory;
    private readonly OptionsCache<TOptions> _cache = new OptionsCache<TOptions>(); 
        
    public OptionsManager(IOptionsFactory<TOptions> factory)
    {
        _factory = factory;
    }

    public TOptions Value => Get(Options.DefaultName);

    public virtual TOptions Get(string name)
    {
        name = name ?? Options.DefaultName;

        if (!_cache.TryGetValue(name, out TOptions options))
        {
            IOptionsFactory<TOptions> localFactory = _factory;
            string localName = name;
            options = _cache.GetOrAdd(name, () => localFactory.Create(localName));
        }

        return options;
    }
}
public class OptionsMonitor<[DynamicallyAccessedMembers(Options.DynamicallyAccessedMembers)] TOptions> 
    : IOptionsMonitor<TOptions>, IDisposable
    where TOptions : class
{
    private readonly IOptionsMonitorCache<TOptions> _cache;
    private readonly IOptionsFactory<TOptions> _factory;
    private readonly List<IDisposable> _registrations = new List<IDisposable>();
    internal event Action<TOptions, string> _onChange;

    public OptionsMonitor(IOptionsFactory<TOptions> factory,
        IEnumerable<IOptionsChangeTokenSource<TOptions>> sources, IOptionsMonitorCache<TOptions> cache)
    {
        _factory = factory;
        _cache = cache;

        void RegisterSource(IOptionsChangeTokenSource<TOptions> source)
        {
            IDisposable registration = ChangeToken.OnChange(
                      () => source.GetChangeToken(),
                      (name) => InvokeChanged(name),
                      source.Name);

            _registrations.Add(registration);
        }
        
        // 此处简写
        foreach (IOptionsChangeTokenSource<TOptions> source in sources)
        {
            RegisterSource(source);
        }
    }

    private void InvokeChanged(string name)
    {
        name = name ?? Options.DefaultName;
        _cache.TryRemove(name);
        TOptions options = Get(name);
        if (_onChange != null)
            _onChange.Invoke(options, name);
    }

    public TOptions CurrentValue
    {
        get => Get(Options.DefaultName);
    }

    public virtual TOptions Get(string name)
    {
        name = name ?? Options.DefaultName;
        return _cache.GetOrAdd(name, () => _factory.Create(name));
    }

    public IDisposable OnChange(Action<TOptions, string> listener)
    {
        var disposable = new ChangeTrackerDisposable(this, listener);
        _onChange += disposable.OnChange;
        return disposable;
    }

    public void Dispose()
    {
        foreach (IDisposable registration in _registrations)
        {
            registration.Dispose();
        }

        _registrations.Clear();
    }
}

选项配置

选项模式提供了IConfigureOptions<>、IConfigureNamedOptions<>和IPostConfigureOptions<>接口,用来对选项进行配置。IConfigureNamedOptions<>继承了IConfigureOptions<>接口,增加了命名选项配置功能。这三个接口中都有一个对选项配置的方法,将接口注入到容器中,当调用工厂创建选项时,会调用接口中的配置方法对选项进行配置。首先会调用IConfigureOptions<>、IConfigureNamedOptions<>接口中的配置方法,然后调用IPostConfigureOptions<>接口中的配置方法。

// OptionsFactory<>
public TOptions Create(string name)
{
    TOptions options = CreateInstance(name);
    foreach (IConfigureOptions<TOptions> setup in _setups)
    {
        if (setup is IConfigureNamedOptions<TOptions> namedSetup)
            namedSetup.Configure(name, options);
        else if (name == Options.DefaultName)
            setup.Configure(options);
    }
    foreach (IPostConfigureOptions<TOptions> post in _postConfigures)
    {
        post.PostConfigure(name, options);
    }

    // 选项验证...

    return options;
}

protected virtual TOptions CreateInstance(string name)
{
    return Activator.CreateInstance<TOptions>();
}

选项验证

选项模式提供了IValidateOptions<>接口,包含一个Validate方法对选项进行验证。将接口注入容器中,当调用工厂创建选项时,会调用接口中的Validate方法对选项进行验证。

// OptionsFactory<>
public TOptions Create(string name)
{
    TOptions options = CreateInstance(name);
    // 选项配置...

    if (_validations.Length > 0)
    {
        var failures = new List<string>();
        foreach (IValidateOptions<TOptions> validate in _validations)
        {
            ValidateOptionsResult result = validate.Validate(name, options);
            if (result is not null && result.Failed)
                failures.AddRange(result.Failures);
        }
        if (failures.Count > 0)
            throw new OptionsValidationException(name, typeof(TOptions), failures);
    }

    return options;
}

如果需要为选项添加验证,实现IValidateOptions<>接口并注入到容器即可:

builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton
    <IValidateOptions<SettingsOptions>, ValidateSettingsOptions>());

也可以添加DataAnnotations验证,调用ValidateDataAnnotations扩展方法即可,该方法定义在 Microsoft.Extensions.Options.DataAnnotations中。需要先调用AddOptions<>扩展方法创建OptionsBuilder<>:

builder.Services.AddOptions<SettingsOptions>()
    .Bind(Configuration.GetSection(SettingsOptions.ConfigurationSectionName))
    .ValidateDataAnnotations();

绑定配置

通过如下扩展方法将选项绑定到配置:

builder.Services.Configure<SettingsOptions>(builder.Configuration.GetSection("Settings"));

绑定到配置是通过IConfiguration.Bind()扩展方法实现的,同时,也添加了对配置修改的监听:文章来源地址https://www.toymoban.com/news/detail-747867.html

public static IServiceCollection Configure<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] TOptions>(this IServiceCollection services, string name, IConfiguration config, Action<BinderOptions> configureBinder) where TOptions : class
{
    if (services == null)
        throw new ArgumentNullException(nameof(services));

    if (config == null)
        throw new ArgumentNullException(nameof(config));

    services.AddOptions();
    services.AddSingleton<IOptionsChangeTokenSource<TOptions>>(
        new ConfigurationChangeTokenSource<TOptions>(name, config));
    return services.AddSingleton<IConfigureOptions<TOptions>>(
        new NamedConfigureFromConfigurationOptions<TOptions>(name, config, configureBinder));
}

到了这里,关于Options选项的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 八、vue_options之computed、watch属性选项

    computed计算属性初体验: 在我们通过Vue调用createApp方法传入一个对象的时候,我们之前写了data属性、methods属性,这一次我们新增一个computed属性,对应的是一个对象,对象里面可以写很多计算属性,fullname是对象里面的key,此时我们需要给他对应一个值,计算属性对应的值是

    2023年04月23日
    浏览(43)
  • CMake入门教程【高级篇】编译选项target_compile_options

    target_compile_options 命令允许用户为特定目标(如可执行文件或库)指定编译器选项,这对于优化构建过程和确保代码兼容性至关重要。

    2024年01月15日
    浏览(40)
  • Golang 中的反射,并用来获取数据类型

    Go语言提供了一种机制在运行中获取某个变量的类型,获取或修改变量的值,调用变量的方法。 示例代码如下 通过 reflect.Value 判断变量类型,并转换成 string 。 输出结果: 输出结果:

    2024年01月22日
    浏览(51)
  • Selenium Webdriver options的实用参数设置

    1、关闭Chrome浏览器受自动控制的提示   2、关闭是否保存密码的弹窗   3、下载文件时自动下载到指定的目录,不要弹出保存文件对话框   4、下载文件完成后不要扫描文件,减少等待的时间   5、关闭是否允许同时下载多个文件的提示   6、跳过网站检测爬虫 注意:这一语句

    2024年01月25日
    浏览(32)
  • 3. MySQL - 数据类型 & 选项约束

    目录 回顾 1. 命令行下的 MySql 客户端 2. 图形化界面的 MySQL-Client 3. 数据库概述 3.1 数据库管理系统是什么 3.2 工作模式 3.3 RDBMS 管理数据的结构 3.4 客户端连接服务器的信息 4. MySQL 中的数据类型 4.1 整型类型 4.2 字符串 4.3 日期/时间 5. MySQL 每个字段的选项约束 6. 练习:将我们

    2024年02月15日
    浏览(59)
  • git中cherry-pick报错是一个合并提交但未提供-m选项的解决方法

    在 new_feature 分支上,想 cherry-pick 一下另一个分支上的提交,不想发生如下错误: 在原始的命令后面加上 -m 1 即可:

    2024年02月15日
    浏览(44)
  • Jmeter书中不会教你的(1)——beanshell用来获取变量和设置变量的vars

           这几年Jmeter用的相对比较多,自己每次在做项目时也常会去翻看以前遇到的问题,记录的一些技巧,解决方案,也一直考虑把它整理出来,一来方便自己查找,二来也希望同行可以互相切磋。我写的这些文章不是针对Jmeter小白用户,可能叙述某些步骤时会直接跳过一

    2024年02月06日
    浏览(48)
  • libcurl是一个用于进行网络通信的开源库,提供了各种功能和选项,可以用于发送和接收HTTP请求、FTP操作等

    libcurl是一个用于进行网络通信的开源库,提供了各种功能和选项,可以用于发送和接收HTTP请求、FTP操作、SMTP邮件等。它支持多种协议,包括HTTP、HTTPS、FTP、FTPS、SMTP、POP3、IMAP等。 以下是libcurl库的一些特点和功能: 跨平台:libcurl可在多个操作系统上使用,包括Windows、Lin

    2024年01月19日
    浏览(49)
  • C# Solidworks二次开发:程序工具界面和选项相关API详解

    大家好,今天要讲的是关于程序工具相关的API介绍。 下面是要介绍的API: (1)第一个为GetAutoPartSimplification,这个API的含义为获取简化配置的指针,下面是官方具体解释: 其输入参数的类型在上一篇文章中已经介绍过了gtError_e,返回值为指向简化配置的指针。 (2)第二个为Ge

    2024年04月17日
    浏览(50)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包