2. Options值的获取
Option值的获取也就是从依赖注入容器中获取相应实现的过程。通过依赖注入阶段,已经知道了IOptions<>和IOptionsSnapshot<>对应的实现是OptionsManager<>,就以OptionsManager<>为例看一下依赖注入后的服务提供过程。OptionsManager<>代码如下:
public class OptionsManager<TOptions> : IOptions<TOptions>, IOptionsSnapshot<TOptions> where TOptions : class, new() { private readonly IOptionsFactory<TOptions> _factory; private readonly OptionsCache<TOptions> _cache = new OptionsCache<TOptions>(); public OptionsManager(IOptionsFactory<TOptions> factory) { _factory = factory; } public TOptions Value { get { return Get(Options.DefaultName); } } public virtual TOptions Get(string name) { name = name ?? Options.DefaultName; return _cache.GetOrAdd(name, () => _factory.Create(name)); } }
它有IOptionsFactory<TOptions>和OptionsCache<TOptions>两个重要的成员。如果直接获取Value值,实际上是调用的另一个Get(string name)方法,传入了空字符串作为name值。所以最终值的获取还是在缓存中读取,这里的代码是_cache.GetOrAdd(name, () => _factory.Create(name)),即如果缓存中存在对应的值,则返回,如果不存在,则由_factory去创建。OptionsFactory<TOptions>的代码如下:
public class OptionsFactory<TOptions> : IOptionsFactory<TOptions> where TOptions : class, new() { private readonly IEnumerable<IConfigureOptions<TOptions>> _setups; private readonly IEnumerable<IPostConfigureOptions<TOptions>> _postConfigures; private readonly IEnumerable<IValidateOptions<TOptions>> _validations; public OptionsFactory(IEnumerable<IConfigureOptions<TOptions>> setups, IEnumerable<IPostConfigureOptions<TOptions>> postConfigures) : this(setups, postConfigures, validations: null) { } public OptionsFactory(IEnumerable<IConfigureOptions<TOptions>> setups, IEnumerable<IPostConfigureOptions<TOptions>> postConfigures, IEnumerable<IValidateOptions<TOptions>> validations) { _setups = setups; _postConfigures = postConfigures; _validations = validations; } public TOptions Create(string name) { var options = new TOptions(); foreach (var setup in _setups) { if (setup is IConfigureNamedOptions<TOptions> namedSetup) { namedSetup.Configure(name, options); } else if (name == Options.DefaultName) { setup.Configure(options); } } foreach (var post in _postConfigures) { post.PostConfigure(name, options); } if (_validations != null) { var failures = new List<string>(); foreach (var validate in _validations) { var result = validate.Validate(name, options); if (result.Failed) { failures.AddRange(result.Failures); } } if (failures.Count > 0) { throw new OptionsValidationException(name, typeof(TOptions), failures); } } return options; } }
主要看它的TOptions Create(string name)方法。这里会遍历它的_setups集合,这个集合类型为IEnumerable<IConfigureOptions<TOptions>>,在讲Options模式的依赖注入的时候已经知道,每一个Configure、ConfigureAll实际上就是向依赖注入容器中注册了一个IConfigureOptions<TOptions>,只是名称可能不同。而PostConfigure和PostConfigureAll方法注册的是IPostConfigureOptions<TOptions>类型,对应的就是_postConfigures集合。
首先会遍历_setups集合,调用IConfigureOptions<TOptions>的Configure方法,这个方法的主要代码就是:
if (Name == null || name == Name) { Action?.Invoke(options); }
如果Name值为null,即对应的是ConfigureAll方法,则执行该Action。或者Name值和需要读取的值相同,则执行该Action。
_setups集合遍历之后,同样的机制遍历_postConfigures集合。这就是上一节关于Configure、ConfigureAll、PostConfigure和PostConfigureAll的执行顺序的验证。
最终返回对应的实例并写入缓存。这就是IOptions和IOptionsSnapshot两种模式的处理机制,接下来看一下IOptionsMonitor模式,它对应的实现是OptionsMonitor。代码如下:
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) { 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); } } public TOptions CurrentValue { get => 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) { var disposable = new ChangeTrackerDisposable(this, listener); _onChange += disposable.OnChange; return disposable; } 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; } }
大部分功能和OptionsManager类似,只是由于它是采用了Singleton模式,所以它是采用监听数据源改变并更新的模式。当通过Configuration作为数据源注册Option的时候,多了一条IOptionsChangeTokenSource的依赖注入。当数据源发生改变的时候更新数据并触发OnChange(Action<TOptions, string> listener),在第一节的数据更新提醒中有相关的例子。