什么是配置
.NET中的配置,本質上就是key-value鍵值對,并且key和value都是字符串類型。
在.NET中提供了多種配置提供程序來對不同的配置進行讀取、寫入、重載等操作,這里我們以為.NET 的源碼項目為例,來看下.NET中的配置主要是有那些類庫。下面這個截圖是.NET 源碼中和配置相關的所有類庫,所有配置相關的類庫都是都是以Microsoft.Extensions.Configuration開頭的。
貌似很多,沒關系我們來簡單理解一下。
類庫名稱 | 類庫作用 |
---|---|
1、Microsoft.Extensions.Configuration.Abstractions | 定義配置相關的接口,其他所有配置類庫都必須引用這個類庫 |
2、Microsoft.Extensions.Configuration | Microsoft.Extensions.Configuration.Abstractions類庫的簡單實現 |
3、配置提供程序:Microsoft.Extensions.Configuration.CommandLine | 基于命令行的配置提供程序,負責對命令行的配置進行讀寫、載入、重載等操作。 |
4、配置提供程序:Microsoft.Extensions.Configuration.EnvironmentVariables | 基于環境變量的配置提供程序,負責對環境變量的配置進行讀寫、載入、重載等操作 |
5、配置提供程序:Microsoft.Extensions.Configuration.FileExtensions | 基于的文件提供程序的基類庫,文件提供程序包括基于Json文件、Ini文件、Xml文件或者自定義文件等。 |
6、配置提供程序:Microsoft.Extensions.Configuration.Json | 基于Json文件的配置提供程序程序,負責從Json文本文件中對配置讀寫、載入、重載等操作。 |
7、配置提供程序:Microsoft.Extensions.Configuration.Ini | 基于Ini文件的配置提供程序,負責從Ini文件中對配置進行讀寫、載入、重載等操作。 |
8、配置提供程序:Microsoft.Extensions.Configuration.UserSecrets | 基于UserSecrets的配置提供程序,這個本質上也是一種基于Json文件類型的配置程序。主要用于管理應用機密 |
9、Microsoft.Extensions.Configuration.Binder | 負責將key-value鍵值對的配置列表綁定到指定的C#實體類上,方便程序使用。 |
從上面可以看到,主要有四個類庫:第1個類庫Abstractions
負責定義配置的一些接口,第2個類庫Configuration
負責定義配置的簡單實現。第3到第8個類庫都是具體的配置提供程序,第9個類庫Binder
負責將配置綁定到指定的的Model,方便程序使用。
配置提供程序,.NET中有多個類庫提供程序,每個類庫提供程序都是以單獨的類庫向外提供,基本上每個類庫就是三個文件,分別是ConfigurationExtensions.cs、ConfigurationProvider.cs和ConfigurationSource.cs,這三個類分別表示配置的擴展方法、配置提供程序和配置源。配置源用于生成配置提供程序。
在第2個類庫中,微軟幫助我們實現了一個基于類庫的配置提供程序,我們在列表中沒有單獨列舉這個類庫提供程序。
配置源IConfigurationSource
配置源表示一個單獨的配置集合,可以表示來自內存的配置源、來自Json文件的配置源。但是配置源不直接提供對配置的訪問操作,它只有一個接口Build
,該接口一個具體的配置提供程序IConfigurationProvider?
,每個配置提供程序負責對配置的讀取、寫入、載入配置、重載配置等訪問操作。
public interface IConfigurationSource
{IConfigurationProvider Build(IConfigurationBuilder builder);
}
配置提供程序IConfigurationProvider
配置提供程序負責實現配置的設置、讀取、重載等功能,并以鍵值對形式提供配置。
public interface IConfigurationProvider
{//讀取配置bool TryGet(string key, out string? value);//修改配置void Set(string key, string? value);//獲取重載配置的TokenIChangeToken GetReloadToken();//載入配置void Load();//獲取指定父路徑下的直接子節點Key,然后 Concat(earlierKeys) 一同返回IEnumerable<string> GetChildKeys(IEnumerable<string> earlierKeys, string? parentPath);
}
配置構建者IConfigurationBuilder
上面的IConfigurationSource和IConfigurationProvider分別表示一種數據源和對一種數據源進行讀寫操作。但是一個程序的配置可能來自很多地方,可能一部分配置來自環境變量、一部分配置來自文件等等。這個時候IConfigurationBuilder配置構建者就誕生了,IConfigurationBuilder接口維護了多個配置源,并提供一個Build方法生成一個統一的配置IConfigurationRoot?
來統一對整個程序的配置進行讀取、寫入、重載等操作。但是這里大家注意,IConfigurationRoot?
對配置的訪問,本質上還是通過配置提供程序IConfigurationProvider
來進行的。
假設,當我們查找一個Key為Name的配置,IConfigurationRoot?
內部會遍歷所有Sources?
屬性生成的IConfigurationProvider
,然后依次調用IConfigurationProvider
的TryGet來獲取Name的具體配置數據。
public interface IConfigurationBuilder
{//保存Build的一些公開的字典屬性,有需要的化可以使用該字段存放一些變量IDictionary<string, object> Properties { get; }//來自多個地方的配置源集合IList<IConfigurationSource> Sources { get; }//向Sources屬性中添加一個配置源IConfigurationBuilder Add(IConfigurationSource source);//基于所有配置源生成一個全局的配置,供程序讀寫,一般我們都是用這個接口對配置進行讀寫。IConfigurationRoot Build();
}
配置構建者實現類ConfigurationBuilder
在具體的配置構建者的Build方法中,我們可以看到,它依次調用IConfigurationProvider
的Buid方法生成多個配置提供程序IConfigurationProvider?
,然后將所有的配置提供程序providers?
傳給了ConfigurationRoot
。ConfigurationRoot
正是調用providers?
的一系列方法實現對配置的讀取、寫入、重載等操作。
public class ConfigurationBuilder : IConfigurationBuilder{private readonly List<IConfigurationSource> _sources = new();public IList<IConfigurationSource> Sources => _sources;public IDictionary<string, object> Properties { get; } = new Dictionary<string, object>();public IConfigurationBuilder Add(IConfigurationSource source){ThrowHelper.ThrowIfNull(source);_sources.Add(source);return this;}public IConfigurationRoot Build(){var providers = new List<IConfigurationProvider>();foreach (IConfigurationSource source in _sources){IConfigurationProvider provider = source.Build(this);providers.Add(provider);}return new ConfigurationRoot(providers);}}
配置接口IConfiguration
這個接口就是最核心的配置接口,提供了對配置的讀取、寫入、重載等操作,它的實現類是ConfigurationRoot
,上面我們已經介紹過,IConfiguration
本身還是通過各個配置提供程序對配置進行訪問操作。
public interface IConfiguration
{//獲取或設置配置string? this[string key] { get; set; }//獲取指定key的配置子節點IConfigurationSection GetSection(string key);//獲取當前配置的直接子節點列表IEnumerable<IConfigurationSection> GetChildren();//當配置發生變更時的tokenIChangeToken GetReloadToken();
}
配置接口IConfigurationRoot
IConfigurationRoot其實是配置的根接口,該接口有個最重要的屬性Providers?
負責保存所有的配置提供程序,IConfiguration
對配置的訪問,就是通過遍歷這個Providers
來訪問的。
public interface IConfigurationRoot : IConfiguration
{//強制重載所有配置void Reload();//所有配置提供程序IEnumerable<IConfigurationProvider> Providers { get; }
}
實現自定義配置提供程序
實現自定義配置提供程序,其實只需要實現三個類就可以,一個是配置源、一個是配置提供程序、一個是針對當前配置的擴展方法。第三個類可有可無,不過我們一般都要實現。我們來參考下基于命令行的配置提供程序類庫的文件。
接下來,我們來實現一個基于數據庫的配置提供程序,分別實現配置源DataBaseConfigurationSource?
、配置提供程序DataBaseConfigurationExtensions?
和擴展方法類DataBaseConfigurationExtensions
,當然在這里我們只做對應的演示,沒有實現具體的配置方法。
public class DataBaseConfigurationSource : IConfigurationSource
{public IConfigurationProvider Build(IConfigurationBuilder builder){return new DataBaseConfigurationProvider();}
}public class DataBaseConfigurationProvider : ConfigurationProvider
{public override void Load(){base.Load();//讀取數據庫配置}
}
public static class DataBaseConfigurationExtensions
{public static IConfigurationBuilder AddDb(this IConfigurationBuilder configurationBuilder){configurationBuilder.Sources.Add(new DataBaseConfigurationSource());return configurationBuilder;}
}
調用自定義配置程序
static void Main(string[] args)
{var builder = new ConfigurationBuilder().AddDb().Build();var value = builder["key"];
}