1. 程式人生 > >.Net Core下如何管理配置檔案

.Net Core下如何管理配置檔案

一、前言

根據該issues來看,System.Configuration在.net core中已經不存在了,那麼取而代之的是由Microsoft.Extensions.Cnfiguration.XXX一系列的類庫提供,對應的開源地址為點選這裡

從當前開源的程式碼來看,在.net core下提供了以下類庫給我們:

以下為提供對哪些方式的支援:

由上面類庫的數量我們可以看出今後我們不會在需要載入更多的庫,只要按需載入就可以了,那麼我們也可以看到新的Configuration提供了更多格式的支援,當然我們自己也可以自行擴充套件以提供對更多格式的支援。下面我們開始進入正文,從頭到尾的學習這5種支援的配置格式。

注意:需要VS2015開發工具

二、正文

首先我們需要建立一個名為“CoreClrConfiguration”的空解決方案,下面將會在這一個解決方案中將下面幾節的內容進行演示。如果讀者只對其中某一個部分感興趣可以直接翻閱到對應的內容下,每個小節都是相互獨立的沒有對應關係。

應該新建哪種專案模板如下圖所示:

A. CommandLine(命令列引數)

在新建專案(名為“CommandLineCfg”)中的project.json中增加以下的依賴(具體版本請讀者根據實際情況決定)

"Microsoft.Extensions.Configuration.CommandLine": "1.0.0-rc1-final",

"Microsoft.Extensions.Configuration.Binder": "1.0.0-rc1-final"

需要注意的是該依賴庫為全域性依賴庫,非dnx451或dnxcore50下的依賴庫。

注:在project.json指定完依賴庫之後需要右擊“引用”->“還原程式包”,之後的部分將不再闡述。

首先我們先舉一個簡單的例子,讀取一個引數的值。首先開啟Program.cs檔案在其中寫入以下的程式:

 1 public static void Main(string[] args)
 2 {
 3         var cmdLineConfig = new
CommandLineConfigurationProvider(args); 4 cmdLineConfig.Load(); 5 6 string value1 = null; 7 cmdLineConfig.TryGet("key1", out value1); 8 9 Console.WriteLine($"key1={value1}"); 10 11 Console.ReadKey(); 12 }

這裡我們可以看到“CommandLineConfigurationProvider”的命名,後面我們介紹的能夠提供對其他格式的支援都會以“xxxConfigurationProvider”來命名的,並且基類都是“ConfigurationProvider”。簡單的介紹完之後下面我們需要開始執行該程式,我們先開啟該專案的屬性,輸入一些測試用的命令列引數:

當然我們這裡只是使用了其中一種支援的格式,其他支援的格式如下:

-Key1=Value1

--Key1=Value1

/Key1=Value1

--Key1 Value1

如果讀者,在應用程式引數中的後面重複寫了“/Key1 Value2”那麼程式中只會採用最後一個設定的值且不區分大小寫。

注:如果命令列引數只傳遞了key而沒有傳遞value那麼在load的時候就會丟擲FormatException異常。

類似於我們常用的ORM一樣,對於外部命令列傳入的引數,key我們其實是可以自己做投影,將對應的key改為我們所希望的key。比如上面的“key1”我們就可以在改為“key2”。而要做到這一效果只需要在建立CommandLineConfigurationProvider的時候利用第二個引數將我們即可,比如下面我們將會將“key1”改為”key2”:

public static void Main(string[] args)
{
       Dictionary<string, string> defaults = new Dictionary<string, string>
       {
                { "--Key1","key2" }
        };

        var cmdLineConfig = new CommandLineConfigurationProvider(args, defaults);
        cmdLineConfig.Load();

        string value1 = null;
        cmdLineConfig.TryGet("key2", out value1);
        Console.WriteLine($"key1&key2={value1}");
            
        Console.ReadKey();
}

其中我們可以看到defaults字典中的key必須加上“--”或“-”否則在呼叫建構函式時將會丟擲ArgumentException異常。最終我們可以看到獲取值的時候是利用後來我們投影之後的key的名稱去獲取而不是“key1”了。

除了通過上面這種方式獲取值之外,還提供了一個通用的模型繫結,能夠將其他的格式轉換為POCO,這樣能夠便於我們更快速的開發,下面筆者將通過兩種方式來講述,首先是利用我們當前已經建立好的Provider來進行Bind,其中POCO如下:

public class CommandLineArgs
{
    public string Key1 { get; set; }
}

Main方法的實現如下:

var cmdLineConfig = new CommandLineConfigurationProvider(args);
var builder = new ConfigurationBuilder();
builder.Add(cmdLineConfig);
var item = builder.Build().Get<CommandLineArgs>();

Console.WriteLine($"key1&key2={item.Key1}");

其中ConfigurationBuilder可以管理多個Provider,比如我們開發一個系統可以將命令列、Json檔案、Ini檔案和XML檔案都新增到其中進行管理。如果我們呼叫了Builder方法,那麼我們就可以利用其返回值統一的獲取我們想要的值,內部會從這些Provider中嘗試獲取我們需要的值如果有值則立即返回,而模型繫結是通過對其擴充套件加進去的。具體擴充套件了以下的方法:

public static class ConfigurationBinder
{
        public static void Bind(this IConfiguration configuration, object instance);
        public static object Get(this IConfiguration configuration, Type type);
        public static object Get(this IConfiguration configuration, Type type, string key);
        public static T Get<T>(this IConfiguration configuration);
        public static T Get<T>(this IConfiguration configuration, T defaultValue);
        public static T Get<T>(this IConfiguration configuration, string key);
        public static T Get<T>(this IConfiguration configuration, string key, T defaultValue);
}

其實現在Microsoft.Extensions.Configuration.Binder中。

另一種方式則是不通過建立CommandLineConfigurationProvider直接利用ConfigurationBuilder實現相同的效果:

var builder = new ConfigurationBuilder();
builder.AddCommandLine(args);
var item = builder.Build().Get<CommandLineArgs>();
Console.WriteLine($"key1={item.Key1}");

其中AddCommandLine也是擴充套件方法,並且下面的四種中都有對應的擴充套件,內部的實現其實就是建立了Provider,比如這個方法的內部實現如下:

1 public static IConfigurationBuilder AddCommandLine(this IConfigurationBuilder configurationBuilder, string[] args)
2 {
3 configurationBuilder.Add(new CommandLineConfigurationProvider(args));
4 return configurationBuilder;
5 }

到這裡關於命令列引數告一段落,如果讀者想了解更多的資訊,可以檢視對應的原始碼以及單元測試。

B. EnvironmentVariables(環境變數)

在新建專案(名為“EnvironmentVariablesCfg”)中的Project.json中增加以下的依賴:

"Microsoft.Extensions.Configuration.EnvironmentVariables": "1.0.0-rc1-final",

"Microsoft.Extensions.Configuration.Binder": "1.0.0-rc1-final"

需要注意的是該依賴庫為全域性依賴庫,非dnx451或dnxcore50下的依賴庫。

跟之前的格式一樣,我們這裡先通過一個簡單的例子來講述如何使用。相信很多新建過.NET Core專案的人都曾看到屬性視窗中的“環境變數”,但是卻不知道如何使用,而本節我們將會利用它讀取對應的配置資訊。首先我們在其中新建一個值:

然後我們開啟Program.cs寫入以下的程式:

public static void Main(string[] args)
{
   var provider = new EnvironmentVariablesConfigurationProvider();
   provider.Load();

   string value = null;
   provider.TryGet("con", out value);
   Console.WriteLine($"con={value}");

   Console.ReadKey();
}

我們可以看到跟上一節的方式是一模一樣的,這樣對於我們今後自己進行擴充套件來說,就避免的我們系統內部的修改。

如果讀者細心檢視provider中的Data會發現其中不僅僅儲存我們的配置資訊還儲存了大量的系統配置資訊,這是因為在呼叫Load的時候內部還將系統配置資訊也讀取了,對應的原始碼如下:

public override void Load()
{
   Load(Environment.GetEnvironmentVariables());
}

在我們初始化provider時可以看到建構函式還支援prefix,那麼下面我們利用prefix來定義一個擁有自己字首的配置避免跟其他的配置資訊相互衝突:

var provider = new EnvironmentVariablesConfigurationProvider("cfg:");
provider.Load();

string value = null;
provider.TryGet("con", out value);
Console.WriteLine($"con={value}");

Console.ReadKey();

讀者還要記得到專案的屬性中將環境配置中的變數的key改成cfg:Con,否則value獲取出來的就是null了。

相信聰明的讀者已經知道對應的如何使用了,所以筆者在這裡只列出對應的程式碼(兩種方式)。

 public class EnvirVarCfg
 {
     public string Con { get; set; }
 }

方式1:

var provider = new EnvironmentVariablesConfigurationProvider("cfg:");
var builder = new ConfigurationBuilder();
builder.Add(provider);
var item = builder.Build().Get<EnvirVarCfg>();
Console.WriteLine($"con={item.Con}");

方式2:

var builder = new ConfigurationBuilder();
builder.AddEnvironmentVariables("cfg:");
var item = builder.Build().Get<EnvirVarCfg>();
Console.WriteLine($"con={item.Con}");

至此環境變數這節就結束了,如果已經掌握規律的讀者下面三節應該很快就能夠掌握了。

C. Ini

在新建的專案(名為“IniCfg”)中的Project.json中增加以下的依賴:

"Microsoft.Extensions.Configuration.Ini": "1.0.0-rc1-final",

"Microsoft.Extensions.Configuration.Binder": "1.0.0-rc1-final"

需要注意的是該依賴庫為全域性依賴庫,非dnx451或dnxcore50下的依賴庫。

首先我們在專案根目錄下新建一個“config.ini”檔案,並在其中寫入如下的內容以便我們讀取:

[SegOne]
Con=localhost

[SegTwo]
Con=192.168.1.113
Ext:Port=5535

[Seg:Three]
Con=192.169.12.12

然後我們開啟Program.cs檔案寫入如下程式碼去讀取配置檔案中的內容:

 1         public static void Main(string[] args)
 2         {
 3             string path = Path.Combine(Directory.GetCurrentDirectory(), "config.ini");
 4             var provider = new IniConfigurationProvider(path);
 5             provider.Load();
 6 
 7             string segoneCon = null;
 8             provider.TryGet("SegOne:Con", out segoneCon);
 9             Console.WriteLine($"SegOne-Con={segoneCon}");
10 
11             string segtwoCon = null;
12             provider.TryGet("SegTwo:Con", out segtwoCon);
13             Console.WriteLine($"SegTwo-Con={segtwoCon}");
14 
15             string segtwoExtPort = null;
16             provider.TryGet("SegTwo:Ext:Port", out segtwoExtPort);
17             Console.WriteLine($"SegTwo-Ext:Port={segtwoExtPort}");
18 
19             string segthreeCon = null;
20             provider.TryGet("Seg:Three:Con", out segthreeCon);
21             Console.WriteLine($"Seg:Three-Con={segthreeCon}");
22 
23             Console.ReadKey();
24         }
View Code

相同很多人都看見過類似的配置檔案,特別是在搭建一些服務的時候,那麼從.net core開始也將原生支援這些配置,當然上面的示例中沒有給出對應的註釋,對應的註釋要以“;”、“#”和“/”開頭即可。

因為在該結構下會存在複雜型別包含複雜型別的情況,所以下面我們的模型可能比較複雜:

 1     public class IniModelCfg
 2     {
 3         public SegOne SegOne { get; set; }
 4         public SegTwo SegTwo { get; set; }
 5         public Seg Seg { get; set; }
 6     }
 7 
 8     public class SegOne
 9     {
10         public string Con { get; set; }
11     }
12 
13     public class SegTwo
14     {
15         public string Con { get; set; }
16         public Ext Ext { get; set; }
17     }
18 
19     public class Ext
20     {
21         public string Port { get; set; }
22     }
23 
24     public class Seg
25     {
26         public Three Three { get; set; }
27     }
28 
29     public class Three
30     {
31         public string Con { get; set; }
32     }
View Code

第一種實現方式:

            string path = Path.Combine(Directory.GetCurrentDirectory(), "config.ini");
            var provider = new IniConfigurationProvider(path);
            var builder = new ConfigurationBuilder();
            builder.Add(provider);
            var item = builder.Build().Get<IniModelCfg>();

            Console.WriteLine($"SegOne-Con={item.SegOne.Con}");
            Console.WriteLine($"SegTwo-Con={item.SegTwo.Con}");
            Console.WriteLine($"SegTwo-Ext:Port={item.SegTwo.Ext.Port}");
            Console.WriteLine($"Seg:Three-Con={item.Seg.Three.Con}");

第二種實現方式:

            string path = Path.Combine(Directory.GetCurrentDirectory(), "config.ini");
            var builder = new ConfigurationBuilder();
            builder.AddIniFile(path);
            var item = builder.Build().Get<IniModelCfg>();

            Console.WriteLine($"SegOne-Con={item.SegOne.Con}");
            Console.WriteLine($"SegTwo-Con={item.SegTwo.Con}");
            Console.WriteLine($"SegTwo-Ext:Port={item.SegTwo.Ext.Port}");
            Console.WriteLine($"Seg:Three-Con={item.Seg.Three.Con}");

D. Json

在新建的專案(名為“JsonCfg”)中的Project.json中增加以下的依賴:

"Microsoft.Extensions.Configuration.Json": "1.0.0-rc1-final",

"Microsoft.Extensions.Configuration.Binder": "1.0.0-rc1-final"

需要注意的是該依賴庫為全域性依賴庫,非dnx451或dnxcore50下的依賴庫。

首先我們在專案根目錄下新建一個“config.json”檔案並在其中寫入如下的內容以便測試:

{
  "url": "localhost",
  "port": {
    "one": 1234,
    "two": 456
  }
}

然後開啟Program.cs檔案並在其中寫入如下內容:

            string path = Path.Combine(Directory.GetCurrentDirectory(), "config.json");
            var provider = new JsonConfigurationProvider(path);
            provider.Load();

            string url = null;
            provider.TryGet("url", out url);
            Console.WriteLine($"url={url}");

            string one = null;
            provider.TryGet("port:one", out one);
            Console.WriteLine($"port-one={one}");


            string two = null;
            provider.TryGet("port:two", out two);
            Console.WriteLine($"port0two={two}");

            Console.ReadKey();

如何獲取某個元素的元素跟Ini方式下是統一的,都是通過冒號來分割。

基本跟之前的還是一樣的,筆者還是會給出對應的程式碼,首先是模型相關的程式碼:

 1     public class JsonModelCfg
 2     {
 3         public string Url { get; set; }
 4         public Port Port { get; set; }
 5     }
 6 
 7     public class Port
 8     {
 9         public string One { get; set; }
10         public string Two { get; set; }
11     }
View Code

第一種實現方式:

            string path = Path.Combine(Directory.GetCurrentDirectory(), "config.json");
            var provider = new JsonConfigurationProvider(path);
            var builder = new ConfigurationBuilder();
            builder.Add(provider);
            var item = builder.Build().Get<JsonModelCfg>();

            Console.WriteLine($"url={item.Url}");
            Console.WriteLine($"port-one={item.Port.One}");
            Console.WriteLine($"port-two={item.Port.Two}");

第二種實現方式:

1             string path = Path.Combine(Directory.GetCurrentDirectory(), "config.json");
2             var builder = new ConfigurationBuilder();
3             builder.AddJsonFile(path);
4             var item = builder.Build().Get<JsonModelCfg>();
5 
6             Console.WriteLine($"url={item.Url}");
7             Console.WriteLine($"port-one={item.Port.One}");
8             Console.WriteLine($"port-two={item.Port.Two}");
View Code

E. Xml

在新建的專案(名為“XmlCfg”)中的Project.json中增加以下的依賴:

"Microsoft.Extensions.Configuration.Xml": "1.0.0-rc1-final",

"Microsoft.Extensions.Configuration.Binder": "1.0.0-rc1-final"

需要注意的是該依賴庫為全域性依賴庫,非dnx451或dnxcore50下的依賴庫。

在專案根目錄下新建一個“config.xml”檔案並在其中寫入如下內容以便後面的測試:

<settings>
  <data>
    <con>123456</con>
  </data>
  <inventory value="test" />
</settings>

然後開啟Program.cs檔案並在其中寫入如下程式碼:

            string path = Path.Combine(Directory.GetCurrentDirectory(), "config.xml");
            var provider = new XmlConfigurationProvider(path);
            provider.Load();

            string con = null;
            provider.TryGet("data:con", out con);
            Console.WriteLine($"con={con}");

            string name = null;
            provider.TryGet("inventory:value", out name);
            Console.WriteLine($"value={name}");

            Console.ReadKey();

對應的模型繫結就不再重複了,完全一模一樣了。至此所有的配置相關的內容就介紹完畢了。