• ASP.NET Core 选项模式源码学习Options IOptionsMonitor(三)


    前言

    IOptionsMonitor 是一种单一示例服务,可随时检索当前选项值,这在单一实例依赖项中尤其有用。IOptionsMonitor用于检索选项并管理TOption实例的选项通知, IOptionsMonitor 支持以下方案:

    • 更改通知
    • 命名选项
    • 可重载配置
    • 选择性选项失效 (IOptionsMonitorCache)

    IOptionsMonitor

    
        public interface IOptionsMonitor<out TOptions>
        {
            /// <summary>
            ///	返回具有 DefaultName 的当前 TOptions 实例。
            /// </summary>
            TOptions CurrentValue { get; }
    
            /// <summary>
            ///	返回具有给定名称的已配置的 TOptions 实例。
            /// </summary>
            TOptions Get(string name);
    
            /// <summary>
            /// 	注册一个要在命名 TOptions 更改时调用的侦听器。
            /// </summary>
            IDisposable OnChange(Action<TOptions, string> listener);
        }
    
    

    OptionsMonitor

    OptionsMonitor通过IOptionsChangeTokenSource实现监听事件

        public class OptionsMonitor<TOptions> : IOptionsMonitor<TOptions>, IDisposable where TOptions : class, new()
        {
            private readonly IOptionsMonitorCache<TOptions> _cache;
            private readonly IOptionsFactory<TOptions> _factory;
            private readonly IEnumerable<IOptionsChangeTokenSource<TOptions>> _sources;
            private readonly List<IDisposable> _registrations = new List<IDisposable>();
            internal event Action<TOptions, string> _onChange;
    
            /// <summary>
            /// Constructor.
            /// </summary>
            /// <param name="factory">The factory to use to create options.</param>
            /// <param name="sources">The sources used to listen for changes to the options instance.</param>
            /// <param name="cache">The cache used to store options.</param>
            public OptionsMonitor(IOptionsFactory<TOptions> factory, IEnumerable<IOptionsChangeTokenSource<TOptions>> sources, IOptionsMonitorCache<TOptions> cache)
            {
                _factory = factory;
                _sources = sources;
                _cache = cache;
    
                foreach (var source in _sources)
                {
                    var registration = ChangeToken.OnChange(
                          () => source.GetChangeToken(),
                          (name) => InvokeChanged(name),
                          source.Name);
    
                    _registrations.Add(registration);
                }
            }
    
            private void InvokeChanged(string name)
            {
                name = name ?? Options.DefaultName;
                _cache.TryRemove(name);
                var options = Get(name);
                if (_onChange != null)
                {
                    _onChange.Invoke(options, name);
                }
            }
    
            /// <summary>
            /// The present value of the options.
            /// </summary>
            public TOptions CurrentValue
            {
                get => Get(Options.DefaultName);
            }
    
            /// <summary>
            /// Returns a configured <typeparamref name="TOptions"/> instance with the given <paramref name="name"/>.
            /// </summary>
            public virtual TOptions Get(string name)
            {
                name = name ?? Options.DefaultName;
                return _cache.GetOrAdd(name, () => _factory.Create(name));
            }
    
            /// <summary>
            /// Registers a listener to be called whenever <typeparamref name="TOptions"/> changes.
            /// </summary>
            /// <param name="listener">The action to be invoked when <typeparamref name="TOptions"/> has changed.</param>
            /// <returns>An <see cref="IDisposable"/> which should be disposed to stop listening for changes.</returns>
            public IDisposable OnChange(Action<TOptions, string> listener)
            {
                var disposable = new ChangeTrackerDisposable(this, listener);
                _onChange += disposable.OnChange;
                return disposable;
            }
    
            /// <summary>
            /// Removes all change registration subscriptions.
            /// </summary>
            public void Dispose()
            {
                // Remove all subscriptions to the change tokens
                foreach (var registration in _registrations)
                {
                    registration.Dispose();
                }
                _registrations.Clear();
            }
    
            internal class ChangeTrackerDisposable : IDisposable
            {
                private readonly Action<TOptions, string> _listener;
                private readonly OptionsMonitor<TOptions> _monitor;
    
                public ChangeTrackerDisposable(OptionsMonitor<TOptions> monitor, Action<TOptions, string> listener)
                {
                    _listener = listener;
                    _monitor = monitor;
                }
    
                public void OnChange(TOptions options, string name) => _listener.Invoke(options, name);
    
                public void Dispose() => _monitor._onChange -= OnChange;
            }
        }
    
    

    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, string> listener)
            {
                var disposable = new ChangeTrackerDisposable(this, listener);
                _onChange += disposable.OnChange;
                return disposable;
            }
    

    通过ChangeTrackerDisposable进行事件的注销

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

    ConfigurationChangeTokenSource

    ConfigurationChangeTokenSource实现IOptionsChangeTokenSource接口

    
        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;
                Name = name ?? Options.DefaultName;
            }
    
           
            public string Name { get; }
    
         
            public IChangeToken GetChangeToken()
            {
                return _config.GetReloadToken();
            }
        }
    
    

    示例

    
        public class WeatherForecastController : ControllerBase
        {
            private readonly ILogger<WeatherForecastController> _logger;
    
            private readonly IOptionsMonitor<MyOptions> _options;
            public WeatherForecastController(IOptionsMonitor<MyOptions> options, ILogger<WeatherForecastController> logger)
            {
                _options = options;
                _logger = logger;
            }
    
            [HttpGet]
            public OkObjectResult Get() {
                _options.OnChange(_=>_logger.LogWarning(_options.CurrentValue.Name));
                return Ok(string.Format("Name:{0},Url:{1}", _options.CurrentValue.Name,_options.CurrentValue.Url));
            }
        }
    
    

    现在我们每次修改配置文件,便会触发OnChange事件

    image

  • 相关阅读:
    2021 RoboCom 世界机器人开发者大赛-本科组(初赛)7-1 懂的都懂 (20 分)
    PTA 乙级 1080 MOOC期终成绩 (25 分) C++
    PTA 乙级 1079 延迟的回文数 (20 分) C++
    PTA 乙级 1078 字符串压缩与解压 (20 分) C++
    PTA 乙级 1077 互评成绩计算 (20 分) C++
    PTA 乙级 1076 Wifi密码 (15 分) python
    PTA 乙级 1075 链表元素分类 (25 分) C++
    Hadoop 代码实现文件上传
    Django学习笔记十---FBV视图--003篇---获取请求信息
    Django学习笔记十---FBV视图--002篇---设置重定向和异常响应
  • 原文地址:https://www.cnblogs.com/yyfh/p/12047289.html
Copyright © 2020-2023  润新知