请选择 进入手机版 | 继续访问电脑版

[ASP.NET] .Net Core设置 Configuration具体 实现

  [复制链接]
查看386 | 回复80 | 2021-9-15 08:10:16 | 显示全部楼层 |阅读模式
目次

迩来 又研究了一下.NetCore设置 选项的源码实现,又学习到了不少东西。这篇文章先写一下IConfiguration的学习成果,Options的后面补上

核心类

  • ConfigurationBuilder:IConfigurationBuilder  (构建IConfiguration)
  • IConfigurationSource (设置 数据泉源 )
  • IConfigurationProvider (将设置 源的原始布局 转为为IDictionary
  • ConfigurationRoot:IConfigurationRoot:IConfiguration  (设置 根节点)

构建

ConfigurationBuilder

下面是ConfigurationBuilder中的紧张 代码

可以看到ConfigurationBuilder的紧张 功能就是设置 数据源到集合中

在Build时依次调用IConfigurationSource的Build函数,并将返回的IConfigurationProvider加入到List中

末了 用IConfigurationProvider的集合构建一个ConfigurationRoot对象

  1. public IList<IConfigurationSource> Sources = new List<IConfigurationSource>();
  2. public IConfigurationBuilder Add(IConfigurationSource source)
  3. {
  4. Sources.Add(source);
  5. return this;
  6. }
  7. public IConfigurationRoot Build()
  8. {
  9. List<IConfigurationProvider> list = new List<IConfigurationProvider>();
  10. foreach (IConfigurationSource source in Sources)
  11. {
  12. IConfigurationProvider item = source.Build(this);
  13. list.Add(item);
  14. }
  15. return new ConfigurationRoot(list);
  16. }
复制代码

IConfigurationSource

  1. public class EnvironmentVariablesConfigurationSource : IConfigurationSource
  2. {
  3. public string Prefix;
  4. public IConfigurationProvider Build(IConfigurationBuilder builder)
  5. {
  6. return new EnvironmentVariablesConfigurationProvider(Prefix);
  7. }
  8. public EnvironmentVariablesConfigurationSource()
  9. {
  10. }
  11. }
  12. public class CommandLineConfigurationSource : IConfigurationSource
  13. {
  14. public IDictionary<string, string> SwitchMappings;
  15. public IEnumerable<string> Args;
  16. public IConfigurationProvider Build(IConfigurationBuilder builder)
  17. {
  18. return new CommandLineConfigurationProvider(Args, SwitchMappings);
  19. }
  20. public CommandLineConfigurationSource()
  21. {
  22. }
  23. }
  24. //JsonConfigurationSource继承自FileConfigurationSource,我这里将其合为一个了
  25. public abstract class JsonConfigurationSource : IConfigurationSource
  26. {
  27. public IFileProvider FileProvider { get; set; }
  28. public string Path { get; set; }
  29. public bool Optional { get; set; }
  30. public bool ReloadOnChange { get; set; }
  31. public int ReloadDelay { get; set; } = 250;
  32. public Action<FileLoadExceptionContext> OnLoadException { get; set; }
  33. public IConfigurationProvider Build(IConfigurationBuilder builder)
  34. {
  35. FileProvider = FileProvider ?? builder.GetFileProvider();
  36. OnLoadException = OnLoadException ?? builder.GetFileLoadExceptionHandler();
  37. return new JsonConfigurationProvider(this);
  38. }
  39. public void ResolveFileProvider()
  40. {
  41. if (FileProvider == null && !string.IsNullOrEmpty(Path) && System.IO.Path.IsPathRooted(Path))
  42. {
  43. string directoryName = System.IO.Path.GetDirectoryName(Path);
  44. string text = System.IO.Path.GetFileName(Path);
  45. while (!string.IsNullOrEmpty(directoryName) && !Directory.Exists(directoryName))
  46. {
  47. text = System.IO.Path.Combine(System.IO.Path.GetFileName(directoryName), text);
  48. directoryName = System.IO.Path.GetDirectoryName(directoryName);
  49. }
  50. if (Directory.Exists(directoryName))
  51. {
  52. FileProvider = new PhysicalFileProvider(directoryName);
  53. Path = text;
  54. }
  55. }
  56. }
  57. }
复制代码

上面展示了比较常用的三种ConfigurationSource,代码都比较简单。

也很轻易 看出来ConfigurationSource的作用就是设置 数据源,并不分析 数据。

分析 数据源的功能由 IConfigurationProvider完成

ConfigurationProvider

下面为IConfigurationProvider接口定义的5个函数

  1. public interface IConfigurationProvider
  2. {
  3. bool TryGet(string key, out string value);
  4. void Set(string key, string value);
  5. IChangeToken GetReloadToken();
  6. void Load();
  7. IEnumerable<string> GetChildKeys(IEnumerable<string> earlierKeys, string parentPath);
  8. }
复制代码

ConfigurationProvider是一个抽象类,继承了IConfigurationProvider接口

在新建Provider时一样平常 都会选择直接继承ConfigurationProvider,接下来看一下ConfigurationProvider的几个核心方法

  1. public abstract class ConfigurationProvider : IConfigurationProvider
  2. {
  3. private ConfigurationReloadToken _reloadToken = new ConfigurationReloadToken();
  4. protected IDictionary<string, string> Data= new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
  5. public virtual bool TryGet(string key, out string value)=>Data.TryGetValue(key, out value);
  6. public virtual void Set(string key, string value)=>Data[key] = value;
  7. public virtual void Load(){}
  8. public IChangeToken GetReloadToken()
  9. {
  10. return _reloadToken;
  11. }
  12. protected void OnReload()
  13. {
  14. ConfigurationReloadToken configurationReloadToken = Interlocked.Exchange(ref _reloadToken, new ConfigurationReloadToken());
  15. configurationReloadToken.OnReload();
  16. }
复制代码

可以推测出:

  • Load函数负责从源数据读取数据然后给字典Data赋值
  • ConfigurationProvider将数据存储在字典Data中,增长 修改都是对字典的操作
  • 每个ConfigurationProvider都会天生 一个IChangeToken,在OnReload函数被调用时天生 新的Token,并调用原Token的OnReload函数

ConfigurationRoot

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

  1. private ConfigurationReloadToken _changeToken = new ConfigurationReloadToken();
  2. public ConfigurationRoot(IList<IConfigurationProvider> providers)
  3. {
  4. _providers = providers;
  5. _changeTokenRegistrations = new List<IDisposable>(providers.Count);
  6. foreach (IConfigurationProvider p in providers)
  7. {
  8. p.Load();
  9. ChangeToken.OnChange(p.GetReloadToken,
  10. delegate{
  11. var oldToken=Interlocked.Exchange(ref _changeToken, new ConfigurationReloadToken());
  12. oldToken.OnReload();
  13. })
  14. }
  15. }
  16. 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函数

  1. public string this[string key]
  2. {
  3. get
  4. {
  5. for (int num = _providers.Count - 1; num >= 0; num--)
  6. {
  7. if (_providers[num].TryGet(key, out var value))
  8. {
  9. return value;
  10. }
  11. }
  12. return null;
  13. }
  14. set
  15. {
  16. foreach (IConfigurationProvider provider in _providers)
  17. {
  18. provider.Set(key, value);
  19. }
  20. }
  21. }
复制代码

GetSection

  1. public IConfigurationSection GetSection(string key)
  2. {
  3. return new ConfigurationSection(this, key);
  4. }
  5. public class ConfigurationSection : IConfigurationSection, IConfiguration
  6. {
  7. private readonly IConfigurationRoot _root;
  8. private readonly string _path;
  9. private string _key;
  10. public string Value
  11. {
  12. get
  13. {
  14. return _root[Path];
  15. }
  16. set
  17. {
  18. _root[Path] = value;
  19. }
  20. }
  21. //ConfigurationPath.Combine = string.Join(":",paramList);
  22. public string this[string key]
  23. {
  24. get
  25. {
  26. return _root[ConfigurationPath.Combine(Path, key)];
  27. }
  28. set
  29. {
  30. _root[ConfigurationPath.Combine(Path, key)] = value;
  31. }
  32. }
  33. public ConfigurationSection(IConfigurationRoot root, string path)
  34. {
  35. _root = root;
  36. _path = path;
  37. }
  38. public IConfigurationSection GetSection(string key)
  39. {
  40. return _root.GetSection(ConfigurationPath.Combine(Path, key));
  41. }
  42. public IEnumerable<IConfigurationSection> GetChildren()
  43. {
  44. return _root.GetChildrenImplementation(Path);
  45. }
  46. public IChangeToken GetReloadToken()
  47. {
  48. return _root.GetReloadToken();
  49. }
  50. }
复制代码

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

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

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

末了 贴上一个从数据加载设置 源并动态更新的小例子

DBConfiguration示例

  1. public void Run()
  2. {
  3. var builder = new ConfigurationBuilder();
  4. var dataProvider = new DBDataProvider();
  5. builder.Sources.Add(new DBConfigurationSource() { DataProvider = dataProvider, ReloadOnChange = true, Table = "config" });
  6. IConfigurationRoot config = builder.Build();
  7. Console.WriteLine(config["time"]);
  8. Task.Run(() =>
  9. {
  10. while (true)
  11. {
  12. Thread.Sleep(2000);
  13. dataProvider.Update("config");
  14. Console.WriteLine($"读取配置时间:{config["time"]}");
  15. }
  16. });
  17. Thread.Sleep(20000);
  18. }
  19. public class DBConfigurationProvider : ConfigurationProvider
  20. {
  21. private DBConfigurationSource Source { get; }
  22. public DBConfigurationProvider(DBConfigurationSource source)
  23. {
  24. Source = source;
  25. }
  26. public override void Load()
  27. {
  28. if (Source.ReloadOnChange)
  29. {
  30. ChangeToken.OnChange(() => Source.DataProvider.Watch(Source.Table), LoadData);
  31. }
  32. LoadData();
  33. }
  34. private void LoadData()
  35. {
  36. var data = Source.DataProvider.GetData(Source.Table);
  37. Load(data);
  38. OnReload();
  39. }
  40. public void Load(Dictionary<string, object> data)
  41. {
  42. var dic = new SortedDictionary<string, string>(StringComparer.OrdinalIgnoreCase);
  43. foreach (var element in data)
  44. {
  45. dic.Add(element.Key, element.Value?.ToString());
  46. }
  47. base.Data = dic;
  48. }
  49. }
  50. public class DBConfigurationSource : IConfigurationSource
  51. {
  52. public DBDataProvider DataProvider { get; set; }
  53. public string Table { get; set; }
  54. public bool ReloadOnChange { get; set; }
  55. public bool Optional { get; set; }
  56. public DBConfigurationSource()
  57. {
  58. }
  59. public IConfigurationProvider Build(IConfigurationBuilder builder)
  60. {
  61. return new DBConfigurationProvider(this);
  62. }
  63. }
  64. public class DBDataProvider
  65. {
  66. private ConcurrentDictionary<string, CancellationTokenSource> tableToken = new ConcurrentDictionary<string, CancellationTokenSource>();
  67. public DBDataProvider()
  68. {
  69. }
  70. public Dictionary<string, object> GetData(string table)
  71. {
  72. switch (table)
  73. {
  74. case "config":
  75. return GetConfig();
  76. }
  77. return new Dictionary<string, object>();
  78. }
  79. public void Update(string table)
  80. {
  81. Console.WriteLine($"更新数据库数据table:{table}");
  82. if (tableToken.TryGetValue(table, out CancellationTokenSource cts))
  83. {
  84. var oldCts = cts;
  85. tableToken[table] = new CancellationTokenSource();
  86. oldCts.Cancel();
  87. }
  88. }
  89. private Dictionary<string, object> GetConfig()
  90. {
  91. var valueDic = new Dictionary<string, object>();
  92. valueDic.TryAdd("time", DateTime.Now.ToString());
  93. valueDic.TryAdd("weather", "windy");
  94. valueDic.TryAdd("people_number:male", 100);
  95. valueDic.TryAdd("people_number:female", 150);
  96. return valueDic;
  97. }
  98. public IChangeToken Watch(string table)
  99. {
  100. var cts = tableToken.GetOrAdd(table, x => new CancellationTokenSource());
  101. return new CancellationChangeToken(cts.Token);
  102. }
  103. }
复制代码

到此这篇关于.Net Core设置 Configuration具体 实现的文章就先容 到这了,更多干系 .Net Core Configuration内容请搜索 脚本之家从前 的文章或继续欣赏 下面的干系 文章渴望 大家以后多多支持脚本之家!


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
回复

使用道具 举报

avatar 蓝色妖姬2017 | 2021-9-15 10:45:24 | 显示全部楼层
admin楼主的病历本丢我这里了!
回复

使用道具 举报

avatar 二只怜雪 | 2021-9-16 02:54:26 | 显示全部楼层
以后要跟admin楼主好好学习学习!
回复

使用道具 举报

avatar 宇宙无限 | 2021-9-23 16:44:00 | 显示全部楼层
有节操!
回复

使用道具 举报

avatar 小野妹子868 | 2021-9-23 23:18:41 | 显示全部楼层
管它三七二十一!
回复

使用道具 举报

avatar 聪聪451 | 2021-9-25 21:41:32 | 显示全部楼层
好东西,学习学习!
回复

使用道具 举报

avatar 豆芽角角123 | 2021-9-25 22:59:42 | 显示全部楼层
楼上的能详细介绍一下么?
回复

使用道具 举报

avatar 干将发硎鞘 | 2021-9-26 17:39:40 | 显示全部楼层
楼上的真不讲道理!
回复

使用道具 举报

avatar 123457735 | 2021-9-28 14:16:05 | 显示全部楼层
信admin楼主,考试不挂科!
回复

使用道具 举报

avatar 李冰381 | 2021-9-29 10:13:31 | 显示全部楼层
以后就跟admin楼主混了!
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则