千家信息网

.Net Core怎么配置Configuration

发表于:2024-10-19 作者:千家信息网编辑
千家信息网最后更新 2024年10月19日,这篇文章主要介绍".Net Core怎么配置Configuration",在日常操作中,相信很多人在.Net Core怎么配置Configuration问题上存在疑惑,小编查阅了各式资料,整理出简单好
千家信息网最后更新 2024年10月19日.Net Core怎么配置Configuration

这篇文章主要介绍".Net Core怎么配置Configuration",在日常操作中,相信很多人在.Net Core怎么配置Configuration问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答".Net Core怎么配置Configuration"的疑惑有所帮助!接下来,请跟着小编一起来学习吧!

目录
  • 核心类

  • 构建

    • ConfigurationBuilder

    • IConfigurationSource

    • ConfigurationProvider

    • ConfigurationRoot

  • 查询

    • 索引器

    • GetSection

  • DBConfiguration示例

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

    核心类

    • ConfigurationBuilder:IConfigurationBuilder (构建IConfiguration)

    • IConfigurationSource (配置数据来源)

    • IConfigurationProvider (将配置源的原始结构转为为IDictionary

    • ConfigurationRoot:IConfigurationRoot:IConfiguration (配置根节点)

    构建

    ConfigurationBuilder

    下面是ConfigurationBuilder中的主要代码

    可以看到ConfigurationBuilder的主要功能就是配置数据源到集合中

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

    最后用IConfigurationProvider的集合构建一个ConfigurationRoot对象

    public IList Sources = new List();public IConfigurationBuilder Add(IConfigurationSource source){    Sources.Add(source);    return this;}public IConfigurationRoot Build(){    List list = new List();    foreach (IConfigurationSource source in Sources)    {        IConfigurationProvider item = source.Build(this);        list.Add(item);    }    return new ConfigurationRoot(list);}

    IConfigurationSource

    public class EnvironmentVariablesConfigurationSource : IConfigurationSource{    public string Prefix;    public IConfigurationProvider Build(IConfigurationBuilder builder)    {        return new EnvironmentVariablesConfigurationProvider(Prefix);    }    public EnvironmentVariablesConfigurationSource()    {    }}       public class CommandLineConfigurationSource : IConfigurationSource{    public IDictionary SwitchMappings;    public IEnumerable Args;    public IConfigurationProvider Build(IConfigurationBuilder builder)    {        return new CommandLineConfigurationProvider(Args, SwitchMappings);    }    public CommandLineConfigurationSource()    {    }}//JsonConfigurationSource继承自FileConfigurationSource,我这里将其合为一个了public abstract class JsonConfigurationSource : IConfigurationSource{ public IFileProvider FileProvider { get; set; } public string Path { get; set; } public bool Optional { get; set; } public bool ReloadOnChange { get; set; } public int ReloadDelay { get; set; } = 250; public Action OnLoadException { get; set; }     public IConfigurationProvider Build(IConfigurationBuilder builder) {  FileProvider = FileProvider ?? builder.GetFileProvider();  OnLoadException = OnLoadException ?? builder.GetFileLoadExceptionHandler();  return new JsonConfigurationProvider(this); }     public void ResolveFileProvider() {  if (FileProvider == null && !string.IsNullOrEmpty(Path) && System.IO.Path.IsPathRooted(Path))  {   string directoryName = System.IO.Path.GetDirectoryName(Path);   string text = System.IO.Path.GetFileName(Path);   while (!string.IsNullOrEmpty(directoryName) && !Directory.Exists(directoryName))   {    text = System.IO.Path.Combine(System.IO.Path.GetFileName(directoryName), text);    directoryName = System.IO.Path.GetDirectoryName(directoryName);   }   if (Directory.Exists(directoryName))   {    FileProvider = new PhysicalFileProvider(directoryName);    Path = text;   }  } }}

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

    也很容易看出来ConfigurationSource的作用就是配置数据源,并不解析数据。

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

    ConfigurationProvider

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

    public interface IConfigurationProvider{ bool TryGet(string key, out string value); void Set(string key, string value); IChangeToken GetReloadToken(); void Load(); IEnumerable GetChildKeys(IEnumerable earlierKeys, string parentPath);}

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

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

    public abstract class ConfigurationProvider : IConfigurationProvider{ private ConfigurationReloadToken _reloadToken = new ConfigurationReloadToken();     protected IDictionary Data= new Dictionary(StringComparer.OrdinalIgnoreCase); public virtual bool TryGet(string key, out string value)=>Data.TryGetValue(key, out value); public virtual void Set(string key, string value)=>Data[key] = value; public virtual void Load(){} public IChangeToken GetReloadToken() {  return _reloadToken; }  protected void OnReload() { ConfigurationReloadToken configurationReloadToken = Interlocked.Exchange(ref _reloadToken, new ConfigurationReloadToken());  configurationReloadToken.OnReload(); }

    可以推测出:

    • Load函数负责从源数据读取数据然后给字典Data赋值

    • ConfigurationProvider将数据存储在字典Data中,增加修改都是对字典的操作

    • 每个ConfigurationProvider都会生成一个IChangeToken,在OnReload函数被调用时生成新的Token,并调用原Token的OnReload函数

    ConfigurationRoot

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

    private ConfigurationReloadToken _changeToken = new ConfigurationReloadToken();public ConfigurationRoot(IList providers){    _providers = providers;    _changeTokenRegistrations = new List(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 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 data)    {        var dic = new SortedDictionary(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 tableToken = new ConcurrentDictionary();    public DBDataProvider()    {    }    public Dictionary GetData(string table)    {        switch (table)        {            case "config":                return GetConfig();        }        return new Dictionary();    }    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 GetConfig()    {        var valueDic = new Dictionary();        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);    }}

    到此,关于".Net Core怎么配置Configuration"的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注网站,小编会继续努力为大家带来更多实用的文章!

    0