ASP.NET Core 選項模式原始碼學習Options IOptionsMonitor(三)
阿新 • • 發佈:2019-12-16
前言
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事件