.Net Core配置Configuration具体实现(2)

在ConfigurationBuilder的Build函数中,我们生成了一个ConfigurationRoot,并给他传递了所有的ConfigrationProvider列表,下面我们看看他用我们的Provider都做了啥吧

private ConfigurationReloadToken _changeToken = new ConfigurationReloadToken(); public ConfigurationRoot(IList<IConfigurationProvider> providers) { _providers = providers; _changeTokenRegistrations = new List<IDisposable>(providers.Count); foreach (IConfigurationProvider p in providers) { p.Load(); ChangeToken.OnChange(p.GetReloadToken, delegate{ var oldToken=Interlocked.Exchange(ref _changeToken, new ConfigurationReloadToken()); oldToken.OnReload(); }) } } public IChangeToken GetReloadToken()=>_changeToken;

上面的代码也对部分地方进行了简化。可以看到ConfigurationRoot在生成时主要就做了两件事

1.调用Provider的Load函数,这会给Provider的Data赋值

2.读取Provider的ReloadToken,每个Provider的Reload事件都会触发ConfigurationRoot自己的ReloadToken的Reload事件

至此配置的数据源构建这块就分析完啦!

查询

常规的配置查询有两种基本方式 :索引器和GetSection(string key)

其余的GetValue等等都是一些扩展方法,本篇文章不对此进行展开研究

索引器

索引器的查询执行的方式是倒叙查询所有的Provider,然后调用Provider的TryGet函数,在查询时重名的Key,最后加入的会生效。

赋值则是依次调用每个Provider的Set函数

public string this[string key] { get { for (int num = _providers.Count - 1; num >= 0; num--) { if (_providers[num].TryGet(key, out var value)) { return value; } } return null; } set { foreach (IConfigurationProvider provider in _providers) { provider.Set(key, value); } } }

GetSection

public IConfigurationSection GetSection(string key) { return new ConfigurationSection(this, key); } public class ConfigurationSection : IConfigurationSection, IConfiguration { private readonly IConfigurationRoot _root; private readonly string _path; private string _key; public string Value { get { return _root[Path]; } set { _root[Path] = value; } } //ConfigurationPath.Combine = string.Join(":",paramList); public string this[string key] { get { return _root[ConfigurationPath.Combine(Path, key)]; } set { _root[ConfigurationPath.Combine(Path, key)] = value; } } public ConfigurationSection(IConfigurationRoot root, string path) { _root = root; _path = path; } public IConfigurationSection GetSection(string key) { return _root.GetSection(ConfigurationPath.Combine(Path, key)); } public IEnumerable<IConfigurationSection> GetChildren() { return _root.GetChildrenImplementation(Path); } public IChangeToken GetReloadToken() { return _root.GetReloadToken(); } }

可以看到GetSection会生成一个ConfigurationSection对象

而ConfigurationSection在读取/设置值时实际上就是对查询的Key用:拼接,然后调用IConfigurationRoot(_root)的赋值或查询函数

关于Configuration的配置和读取的知识点大概就是以上这些了,还有更深入的涉及到对象的绑定这一块Get<> Bind<> GetChildren()等,比较难读,要一行一行代码看,以后有时间可能再研究一下

最后贴上一个从数据加载配置源并动态更新的小例子

DBConfiguration示例

public void Run() { var builder = new ConfigurationBuilder(); var dataProvider = new DBDataProvider(); builder.Sources.Add(new DBConfigurationSource() { DataProvider = dataProvider, ReloadOnChange = true, Table = "config" }); IConfigurationRoot config = builder.Build(); Console.WriteLine(config["time"]); Task.Run(() => { while (true) { Thread.Sleep(2000); dataProvider.Update("config"); Console.WriteLine($"读取配置时间:{config["time"]}"); } }); Thread.Sleep(20000); } public class DBConfigurationProvider : ConfigurationProvider { private DBConfigurationSource Source { get; } public DBConfigurationProvider(DBConfigurationSource source) { Source = source; } public override void Load() { if (Source.ReloadOnChange) { ChangeToken.OnChange(() => Source.DataProvider.Watch(Source.Table), LoadData); } LoadData(); } private void LoadData() { var data = Source.DataProvider.GetData(Source.Table); Load(data); OnReload(); } public void Load(Dictionary<string, object> data) { var dic = new SortedDictionary<string, string>(StringComparer.OrdinalIgnoreCase); foreach (var element in data) { dic.Add(element.Key, element.Value?.ToString()); } base.Data = dic; } } public class DBConfigurationSource : IConfigurationSource { public DBDataProvider DataProvider { get; set; } public string Table { get; set; } public bool ReloadOnChange { get; set; } public bool Optional { get; set; } public DBConfigurationSource() { } public IConfigurationProvider Build(IConfigurationBuilder builder) { return new DBConfigurationProvider(this); } } public class DBDataProvider { private ConcurrentDictionary<string, CancellationTokenSource> tableToken = new ConcurrentDictionary<string, CancellationTokenSource>(); public DBDataProvider() { } public Dictionary<string, object> GetData(string table) { switch (table) { case "config": return GetConfig(); } return new Dictionary<string, object>(); } public void Update(string table) { Console.WriteLine($"更新数据库数据table:{table}"); if (tableToken.TryGetValue(table, out CancellationTokenSource cts)) { var oldCts = cts; tableToken[table] = new CancellationTokenSource(); oldCts.Cancel(); } } private Dictionary<string, object> GetConfig() { var valueDic = new Dictionary<string, object>(); valueDic.TryAdd("time", DateTime.Now.ToString()); valueDic.TryAdd("weather", "windy"); valueDic.TryAdd("people_number:male", 100); valueDic.TryAdd("people_number:female", 150); return valueDic; } public IChangeToken Watch(string table) { var cts = tableToken.GetOrAdd(table, x => new CancellationTokenSource()); return new CancellationChangeToken(cts.Token); } }

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zysfjd.html