欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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

程序员文章站 2023-08-31 21:56:57
前言 IOptionsMonitor 是一种单一示例服务,可随时检索当前选项值,这在单一实例依赖项中尤其有用。IOptionsMonitor用于检索选项并管理TOption实例的选项通知, IOptionsMonitor 支持以下方案: 更改通知 命名选项 可重载配置 选择性选项失效 (IOptio ......

前言

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事件

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