.Net Core中的配置文件源碼解析

一、配置簡述

  之前在.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 ,就能獲取所有的配置。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章