您的位置:首页 > 编程语言 > ASP

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

2020-01-14 10:09 405 查看

前言

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

  • 点赞 1
  • 收藏
  • 分享
  • 文章举报
HueiFeng 发布了50 篇原创文章 · 获赞 53 · 访问量 2254 私信 关注
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: