一、配置簡述
之前在.Net Framework平臺開發時,一般配置文件都是xml格式的Web.config,而需要配置其他格式的文件就需要自己去讀取內容,加載配置了。.而Net Core支持從命令行、環境變量、文件、內存、Key-per-file中加載配置,其中文件包括xml、ini、json三種文件格式。這裏需要說明一下,不論哪種格式的配置文件,加載到程序中最終會以Key/Value形式保存,源碼中將所有配置讀取出來並保存在 Dictionary<string, string> Data 的字典中。額外說明一下Key-per-file配置方式是以文件名稱爲key,文件內容爲value的形式。
二、源碼解析
點擊查看源碼,我畫了一個主要類之間的邏輯關係圖,如下:
實線表示繼承關係,每條虛線表示意義已經在上面表明。可以分爲4個部分,最終要構建的就是IConfigurationRoot,因爲所有的配置都存儲在它裏面的Providers集合中的Data的字典中。
構建它兩條路徑,一條通過IConfigurationSource構建IConfigurationProvider,然後通過IConfigurationProvider集合構建IConfigurationRoot,也就是圖上標記的1/2兩步。
另一條是通過IConfigurationSource集合構建IConfigurationBuilder,在通過Builder方法遍歷循環創建創建IConfigurationProvider集合,在通過IConfigurationProvider集合構建IConfigurationRoot。也就是圖上的3/4兩步。
這裏只講文件配置方式,包括xml、ini、json文件。
IConfigurationSource:
它是配置文件的根本,它表示配置文件本身,比如繼承自它的 FileConfigurationSource 裏面有個Path屬性,表示文件路徑,會通過這個Path讀取這個文件內容。如我們常用的xml、json、ini文件配置都繼承自 FileConfigurationSource 。
IConfigurationProvider:
前面說了,配置最終會轉換爲key/value的形式,而 IConfigurationProvider 就是這個裝換。比如繼承他的 FileConfigurationProvider ,它的構造函數需要傳第一個 FileConfigurationSource , FileConfigurationProvider 會根據 FileConfigurationSource 的 path 屬性找到對應文件,然後讀取配置文件到它的 IDictionary<string, string> Data 中。xml的讀取在 XmlConfigurationProvider 中,json文件的讀取在 JsonConfigurationProvider 。
private void Load(bool reload) { //刪除一些判斷邏輯 if (reload) { Data = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); } using (var stream = file.CreateReadStream()) { //這一步會將文件裏面的配置 放到Data中 不同的文件格式Load方法都有對應的實現,這裏就不細看了。 Load(stream); } OnReload(); }
一個 IConfigurationProvider 的實現類中會包含一個對應的 IConfigurationSource 的實現類,比如 JsonConfigurationProvider 類中包含一個 JsonConfigurationProvider 。
IConfigurationRoot:
所有的類最終構建的目標就是IConfigurationRoot,它包含一個 IList<IConfigurationProvider> _providers 集合,而每一個 IConfigurationProvider 包含一個 IDictionary<string, string> Data ,所以現在你現在是不是有提花灌頂的感覺。這樣做的目的是一個程序可能有多個配置文件,可能有一個xml文件、一個json文件、一個Ini文件。每個配置文件會被讀取到對應的 ConfigurationSource 中,然後通過它構建對應的 ConfigurationProvider ,然後用三個 ConfigurationProvider 構建 ConfigurationRoot 。
那這樣會有一個問題,比如xml文件配置了 A的值爲1,json文件也配置名稱爲A的值爲2,那會獲取誰的值呢? 下面源碼可以看到,IConfigurationRoot會反轉添加的順序,循環遍歷,如果找到就返回,所以後面添加的會覆蓋前面添加的。
public string this[string key] { get { //_providers也就是IConfigurationRoot裏面的IList<IConfigurationProvider> foreach (var provider in _providers.Reverse()) { string value; if (provider.TryGet(key, out value)) { return value; } } return null; } set { if (!_providers.Any()) { throw new InvalidOperationException(Resources.Error_NoSources); } foreach (var provider in _providers) { provider.Set(key, value); } } }
對於Json、xml、ini文件,比如
{ "user": { "address": { "Provice": "浙江省", "city": "杭州市" }, "name": "張三" } }
在Data中存儲的key爲 user:address:Province、user:address:city和user:name。中間以“:”分割,這個“:”是固定只讀的,不可以改變的。
三、簡單使用
創建一個WebApi項目,修改Program代碼如下:
public static void Main(string[] args) { var configRoot = new ConfigurationBuilder() .SetBasePath(Directory.GetCurrentDirectory())//設置基礎路徑 .AddJsonFile("a.json")//加載配置 .AddXmlFile("b.xml") .Build(); var builder = new WebHostBuilder() .UseConfiguration(configRoot) .UseContentRoot(Directory.GetCurrentDirectory()) .UseKestrel() .UseStartup<Startup>() .Build(); builder.Run(); }
然後當用的時候,只需要注入 IConfiguration ,就能獲取所有的配置。