zoukankan      html  css  js  c++  java
  • ASP.NET Core 2.1 源码学习之 Options[3]:IOptionsMonitor

    前面我们讲到 IOptionsIOptionsSnapshot,他们两个最大的区别便是前者注册的是单例模式,后者注册的是 Scope 模式。而 IOptionsMonitor 则要求配置源必须是可监听的,用来实现 Options 实例的自动更新,并对外提供了 OnChage 事件,给我们更多的控制权。

    目录

    1. IOptionsMonitor
    2. OptionsMonitor源码探索
    3. ConfigurationChangeTokenSource
    4. 示例

    IOptionsMonitor

    对于 IOptionsMonitor 我们接触的较少,它的定义如下:

    public interface IOptionsMonitor<out TOptions>
    {
        TOptions CurrentValue { get; }
    
        TOptions Get(string name);
    
        IDisposable OnChange(Action<TOptions, string> listener);
    }
    

    AddOptions扩展方法中,可以看到它的默认实现为 OptionsMonitor

    public static IServiceCollection AddOptions(this IServiceCollection services)
    {
        services.TryAdd(ServiceDescriptor.Singleton(typeof(IOptionsMonitor<>), typeof(OptionsMonitor<>)));
        services.TryAdd(ServiceDescriptor.Transient(typeof(IOptionsFactory<>), typeof(OptionsFactory<>)));
        services.TryAdd(ServiceDescriptor.Singleton(typeof(IOptionsMonitorCache<>), typeof(OptionsCache<>)));
    }
    

    OptionsMonitor源码探索

    OptionsMonitor通过IOptionsChangeTokenSource来实现事件的监听,具体如下:

    public class OptionsMonitor<TOptions> : IOptionsMonitor<TOptions> where TOptions : class, new()
    {
        private readonly IOptionsMonitorCache<TOptions> _cache;
        private readonly IOptionsFactory<TOptions> _factory;
        private readonly IEnumerable<IOptionsChangeTokenSource<TOptions>> _sources;
        internal event Action<TOptions, string> _onChange;
    
        public OptionsMonitor(IOptionsFactory<TOptions> factory, IEnumerable<IOptionsChangeTokenSource<TOptions>> sources, IOptionsMonitorCache<TOptions> cache)
        {
            _factory = factory;
            _sources = sources;
            _cache = cache;
    
            foreach (var source in _sources)
            {
                ChangeToken.OnChange<string>(() => source.GetChangeToken(), (name) => InvokeChanged(name), source.Name);
            }
        }
    
        public TOptions CurrentValue => 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)
        {
            ...
        }
    
        private void InvokeChanged(string name)
        {
            ...
        }
    }
    

    首先看构造函数中的三个参数,其中 IOptionsFactory<>在上一章已讲过,而IOptionsChangeTokenSource则在 第一章 中介绍过,通过IConfiguration进行配置的 Options,会注册该类型的实现,用来实现对配置源的监听:

    public static IServiceCollection Configure<TOptions>(this IServiceCollection services, string name, IConfiguration config) where TOptions : class
    {
        ...
        services.AddSingleton<IOptionsChangeTokenSource<TOptions>>(new ConfigurationChangeTokenSource<TOptions>(name, config));
        ...
    }
    

    IOptionsChangeTokenSource 的定义如下:

    public interface IOptionsChangeTokenSource<out TOptions>
    {
        IChangeToken GetChangeToken();
    
        string Name { get; }
    }
    

    OptionsMonitor的构造函数中,通过调用其GetChangeToken方法,获取到 ChangeToken ,在 InvokeChanged 完成 _onChange 事件的调用:

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

    而对外暴露的OnChange方法,方便我们注册自己的逻辑:

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

    这里又使用了一个 ChangeTrackerDisposable 的包装类,用来实现事件的注销:

    internal class ChangeTrackerDisposable : IDisposable
    {
        private readonly Action<TOptions> _listener;
        private readonly OptionsMonitor<TOptions> _monitor;
    
        public ChangeTrackerDisposable(OptionsMonitor<TOptions> monitor, Action<TOptions> listener)
        {
            _listener = listener;
            _monitor = monitor;
        }
    
        public void OnChange(TOptions options) => _listener.Invoke(options);
    
        public void Dispose() => _monitor._onChange -= OnChange;
    }
    

    构造函数的最后一个参数IOptionsMonitorCache的默认实现便是 上一章 中介绍的 OptionsCache

    ConfigurationChangeTokenSource

    IConfigurationChangeTokenSource 的默认实现类是 ConfigurationChangeTokenSource :

    public class ConfigurationChangeTokenSource<TOptions> : IOptionsChangeTokenSource<TOptions>
    {
        private IConfiguration _config;
    
        public ConfigurationChangeTokenSource(IConfiguration config) : this(Options.DefaultName, config) { }
    
        public ConfigurationChangeTokenSource(string name, IConfiguration config)
        {
            if (config == null)
            {
                throw new ArgumentNullException(nameof(config));
            }
            _config = config;
        }
    
        public string Name { get; }
    
        public IChangeToken GetChangeToken()
        {
            return _config.GetReloadToken();
        }
    }
    

    上面用到的 ChangeToken 便是通过构造函数接受的IConfiguration类型的参数来获取的:

    public interface IConfiguration
    {
        ...
    
        IChangeToken GetReloadToken();
    
        ...
    }
    

    因此要想使用IOptionsMonitor,通常要使用IConfiguration来注册才可以,当然,你也可以实现自己的 ConfigurationChangeTokenSource

    示例

    下面简单演示一下IOptionsMonitor的使用:

    首先创建一个控制台程序,添加appsettings.json

    {
      "Name": "bob"
    }
    

    然后修改Program.cs如下:

    public class MyOptions
    {
        public string Name { get; set; }
    }
    
    class Program
    {
        private IOptionsMonitor<MyOptions> _options;
    
        public Program()
        {
            var configuration = new ConfigurationBuilder()
                .SetBasePath(Directory.GetCurrentDirectory())
                .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
                .Build();
    
            var serviceCollection = new ServiceCollection();
    
            serviceCollection.Configure<MyOptions>(configuration);
    
            var serviceProvider = serviceCollection.BuildServiceProvider();
            _options = serviceProvider.GetRequiredService<IOptionsMonitor<MyOptions>>();
        }
    
        public static void Main(string[] args)
        {
            new Program().Execute(args);
        }
    
        public void Execute(string[] args)
        {
            Console.WriteLine(_options.CurrentValue.Name);
            _options.OnChange(_ => Console.WriteLine(_.Name));
            Console.ReadKey();
        }
    }
    

    我们手动修改配置文件,便会触发OnChange事件。

    附示例代码地址:https://github.com/RainingNight/AspNetCoreSample/tree/master/src/OptionsSample

    总结

    本章介绍了 IOptionsMonitor 的实现:通过 IConfiguration 所提供的 ChangeToken ,来注册监听事件,对其 CurrentValue 进行更新。到此,ASP.NET Core 中的 Options 源码也就分析完了,其本身比较简单,并没有太多东西。更具体的可以去 Github 上看完整的源码。

  • 相关阅读:
    VMware Workstation虚拟机Ubuntu中实现与主机共享(复制和粘贴)
    虚拟机 VMware Workstation12 安装Ubuntu系统
    虚拟机 VMware Workstation12 安装OS X 系统
    ASP.NET Core学习链接
    Java开发中的23种设计模式详解
    C#线程同步的几种方法
    FTP webReq.ContentType异常的处理
    大小端 Big-Endian 与 Little-Endian
    C++:运算符重载函数之"++"、"--"、"[ ]"、"=="的应用
    C++:成员运算符重载函数和友元运算符重载函数的比较
  • 原文地址:https://www.cnblogs.com/RainingNight/p/strongly-typed-options-ioptions-monitor-in-asp-net-core.html
Copyright © 2011-2022 走看看