1. 程式人生 > >asp.net core 系列之Configuration

asp.net core 系列之Configuration

在ASP.NET Core中的App configuration 是通過configuration providers基於key-value對建立的。Configuration providers讀取配置檔案到key-value,從多種配置源中:

  • Azure key Vault
  • Command-line arguments
  • Custom providers(installed or created)
  • Directory files
  • Environment variables
  • In-memory .NET objects
  • Setting files

用於提供Configuration配置的包是包含在Microsoft.AspNetCore.App metapackage裡。下面的程式碼示例,將會使用Microsoft.Extensions.Configuration名稱空間。

using Microsoft.Extensions.Configuration;

一.概述

1.Host vs App configuration(對比)

在應用配置和啟動之前,host被配置和launched(發動,開展)。Host 負責應用的startup和生命週期管理。應用和主機都是用這個主題描述的configuration providers來配置。主機配置的key-values對成為應用全域性配置的一部分。

2.Default configuration

ASP.NET Core基礎上的Web應用 dotnet new templates(模板)會呼叫CreateDefaultBuilder,當建立host時,CreateDefaultBuilder為應用提供預設的配置,以下面的順序:

(1).主機配置是被下面這些提供的:

  • 以ASPNETCORE_為字首的環境變數(例如,ASPNETCORE_ENVIRONMENT)使用Environment Variables Configuration Provider. 當configuration key-values對被載入時,字首(ASPNETCORE_)被去掉。
  • 命令列引數使用Command-line Configuration Provider提供

(2).應用配置是被下面這些提供的:

  • appsettings.json使用File Configuration Provider提供
  • appsetting.{Environment}.json使用File Configuration Provider提供
  • 當應用執行在Development environment(開發環境), Secret Manager使用entry程式集(entry:入口)
  • 環境變數使用Environment Variables Configuration Provider. 如果一個自定義字首被使用了,這個字首會在configuration key-value pairs被載入時去掉。(例如, PREFIX_ with .AddEnvironmentVariables(prefix:”PREFIX_”)).
  • Command-line 引數使用command-line Configuration Provider

3.Security

採用下面的最佳實踐:

  • 不要儲存密碼或者其他敏感資料在configuration provider code或者 plain text configuration files.
  • 不要在開發和測試環境使用production secrets
  • 在專案外,指定secrets,以便它們不會被意外提交到原始碼倉庫

4.Hierarchical configuration data(分層的配置資料)

在下面的JSON檔案中,結構化分層的兩個sections中存在四個key:

{
  "section0": {
    "key0": "value",
    "key1": "value"
  },
  "section1": {
    "key0": "value",
    "key1": "value"
  }
}

當檔案被讀取到配置中時,唯一的key被建立,來維護原始配置源中的分層資料結構。

Section和key被使用冒號展開來維持原始結構:

section0:key0

section0:key1

section1:key0

section1:key1

如上,每個值都可以被唯一的取到。

GetSection和GetChildren方法可以被用來分離配置資料中的sections和section的children 。這些方法會在隨後的GetSection,GetChildren,和Exists描述。GetSection是在Microsoft.Extensions.Configuration包中,這個包是在Microsoft.AspNetCore.App metapackage.

5.Conventions(習慣,約定)

這裡是一些習慣的約定。

在應用啟動時,配置源按照它們的configuration provider被指定的順序來被讀取。

Configuration providers實現了變化檢測,Configuration providers可以重新載入配置,當一個基礎的設定被改變時。例如,File Configuration Provider和Azure key Value Configuration Provider實現了變化檢測。

IConfiguration在應用的依賴注入(DI)容器中是可用的。IConfiguration可以被注入到一個Razor Pages Pagemodel來包含一個配置的類。

public class IndexModel : PageModel
{
    private readonly IConfiguration _config;

    public IndexModel(IConfiguration config)
    {
        _config = config;
    }

    // The _config local variable is used to obtain configuration 
    // throughout the class.
}

Configuration providers不能使用DI,因為當它們(Configuration providers)被host建立時,DI是不可用的。

Configuration keys採用下面的約定:

  • Keys忽略大小寫。例如ConnectionString和connectionstring被認為是相同的鍵(keys)
  • If a value for the same key is set by the same or different configuration providers, the last value set on the key is the value used.(如果同一個key的值被不同的資料提供器設定,會預設使用後面設定的,按configuration providers指定的順序)
  • 分層keys
    • 在Configuration API中,冒號:可以在所有平臺起作用
    • 在環境變數中,冒號可能不能在所有平臺起作用。雙下劃線(__)被所有平臺支援並且轉化為冒號
    • 在Azure Key Vault,分層的keys用--(兩個中槓)作為分割符,你必須提供程式碼來用冒號替換這兩個中槓。當secrets被載入到應用的配置中時。
  • ConfigurationBinder支援繫結陣列到物件,在配置keys中使用array 標識體。陣列繫結會在Bind an array to class節描述。

Configuratian values採用下面的約定:

  • Values都是string
  • Null值不能被儲存在配置中或者繫結到物件

6.Providers

下面列出了ASP.NET Core應用可用的configuration providers:

Configuration sources(配置源)按照它們的configuration providers在startup中指定的順序被順序讀取。

典型的configuration providers的順序:

  1. Files (appsettings.json, appsettings.{Environment}.json, {Environment}是應用當前的執行環境)
  2. Azure Key Vault
  3. User secrets (Secret Manager)(僅用在開發環境)
  4. Environment variables
  5. Command-line arguments

這是一種通用的實踐,把命令列配置源放到最後,使它可以重寫其他配置源設定的配置。

注意:後面的配置會覆蓋前面的配置

7.ConfigureAppConfiguration

呼叫ConfigureAppConfiguration,當需要建立host來指定除了被CreateDefaultBuilder自動新增配置源外的其他配置源:

public class Program
{
    public static Dictionary<string, string> arrayDict = 
        new Dictionary<string, string>
        {
            {"array:entries:0", "value0"},
            {"array:entries:1", "value1"},
            {"array:entries:2", "value2"},
            {"array:entries:4", "value4"},
            {"array:entries:5", "value5"}
        };

    public static void Main(string[] args)
    {
        CreateWebHostBuilder(args).Build().Run();
    }

    public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
        WebHost.CreateDefaultBuilder(args)
            .ConfigureAppConfiguration((hostingContext, config) =>
            {
                config.SetBasePath(Directory.GetCurrentDirectory());
                config.AddInMemoryCollection(arrayDict);
                config.AddJsonFile(
                    "json_array.json", optional: false, reloadOnChange: false);
                config.AddJsonFile(
                    "starship.json", optional: false, reloadOnChange: false);
                config.AddXmlFile(
                    "tvshow.xml", optional: false, reloadOnChange: false);
                config.AddEFConfiguration(
                    options => options.UseInMemoryDatabase("InMemoryDb"));
                config.AddCommandLine(args);
            })
            .UseStartup<Startup>();
}

二.configuration provider講解

8.Command-line Configuration Provider

在執行時,CommandLineConfigurationProviders從command line argument key-value pairs中載入配置.

要想啟動command-line 配置,AddCommandLine擴充套件方法需要在ConfigurationBuilder例項中被呼叫。

AddCommandLine早已被CreateDefaultBuilder呼叫。如果你需要提供 app configuration 並且仍然可以用command-line arguments重寫配置,在ConfigureAppConfiguration中,呼叫app的額外的providers,並且在最後呼叫AddCommandLine.

public class Program
{
    public static void Main(string[] args)
    {
        CreateWebHostBuilder(args).Build().Run();
    }

    public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
        WebHost.CreateDefaultBuilder(args)
            .ConfigureAppConfiguration((hostingContext, config) =>
            {
                // Call other providers here and call AddCommandLine last.
                config.AddCommandLine(args);
            })
            .UseStartup<Startup>();
}

當直接建立一個WebHostBuilder時,呼叫UseConfiguration(另一種用法):

var config = new ConfigurationBuilder()
    // Call additional providers here as needed.
    // Call AddCommandLine last to allow arguments to override other configuration.
    .AddCommandLine(args)
    .Build();

var host = new WebHostBuilder()
    .UseConfiguration(config)
    .UseKestrel()
    .UseStartup<Startup>();

Example

示例應用功能利用靜態方法CreateDefaultBuilder來建立host,它包含對AddCommandLine的呼叫.

  1. 在專案目錄開啟命令列
  2. 把命令列引數用到 dotnet run 命令,dotnet run CommandLineKey=CommandLineValue
  3. 在應用執行之後,開啟瀏覽器到應用的 http://localhost:5000
  4. 觀察輸出(dotnet run)。

Arguments

值必須是下面的形式.=後面的值可以為null(例如,CommandLineKey=)

在同一個命令視窗中,不要混合使用=號和空格

dotnet run CommandLineKey1=value1 --CommandLineKey2=value2 /CommandLineKey3=value3
dotnet run --CommandLineKey1 value1 /CommandLineKey2 value2
dotnet run CommandLineKey1= CommandLineKey2=value2

Switch mappings

 Switch mapping dictionary key rules:

  • Swithes必須以-或者- - 開頭
  • Swith mappings不能包含重複的keys

當建立host指定應用配置時,呼叫ConfigureAppConfiguration:

public class Program
{
    public static readonly Dictionary<string, string> _switchMappings = 
        new Dictionary<string, string>
        {
            { "-CLKey1", "CommandLineKey1" },//把命令列中key為-CLKey1的鍵替換為key為CommandLineKey1
            { "-CLKey2", "CommandLineKey2" }
        };

    public static void Main(string[] args)
    {
        CreateWebHostBuilder(args).Build().Run();
    }

    // Do not pass the args to CreateDefaultBuilder
    public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
        WebHost.CreateDefaultBuilder()
            .ConfigureAppConfiguration((hostingContext, config) =>
            {
                config.AddCommandLine(args, _switchMappings);
            })
            .UseStartup<Startup>();
}

當switch mappings dictionary被建立後,包含下面的資料:

如果啟動應用時,switch-mapped keys被使用,configuration會在dictionary提供的key裡接收到配置值。

dotnet run -CLKey1=value1 -CLKey2=value2

在執行之前的命令後,configuration包含了值,如下表:

如上,可以看出Switch mappings的作用是:

 把命令列中輸入的key替換為switch mapping中的key值。

9.Environment Variables Configuration Provider

要啟用environment variables configuration,需要呼叫AddEnvironmentVariables擴充套件方法。

AddEnvironmentVariables用來載入以ASPNETCORE_開頭的環境變數。

當建立host指定應用配置時,呼叫ConfigureAppConfiguration :

public class Program
{
    public static void Main(string[] args)
    {
        CreateWebHostBuilder(args).Build().Run();
    }

    public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
        WebHost.CreateDefaultBuilder(args)
            .ConfigureAppConfiguration((hostingContext, config) =>
            {
                // Call additional providers here as needed.
                // Call AddEnvironmentVariables last if you need to allow
                // environment variables to override values from other 
                // providers.
                config.AddEnvironmentVariables(prefix: "PREFIX_");
            })
            .UseStartup<Startup>();
}

當直接建立WebHostBuilder,呼叫UseConfiguration:

var config = new ConfigurationBuilder()
    .AddEnvironmentVariables()
    .Build();

var host = new WebHostBuilder()
    .UseConfiguration(config)
    .UseKestrel()
    .UseStartup<Startup>();

Example

示例應用功能利用靜態方法CreateDefaultBuilder來建立host,它包含對AddEnvironmentVariables的呼叫.

  1. 執行示例應用。瀏覽 http://localhost:5000
  2. 觀察環境變數的輸出。

環境變數以下面的開頭:

  • ASPNETCORE_
  • urls
  • Logging
  • ENVIRONMENT
  • contentRoot
  • AllowedHosts
  • applicationName
  • CommandLine

如果你希望在應用中暴露這些環境變數可用,在Pages/Index.cshtml.cs中改變FilteredConfiguration為下面:

FilteredConfiguration = _config.AsEnumerable();

Prefixes

當你應用一個字首到AddEnvironmentVariables方法,應用配置中的環境變數可以被過濾。例如,在字首CUSTOM_上過濾環境變數,應用字首到configuration provider:

var config = new ConfigurationBuilder()
    .AddEnvironmentVariables("CUSTOM_")
    .Build();

字首會被分離,當配置key-values pairs被建立時。

靜態方法CreateDefaultBuilder會建立一個WebHostBuilder來建立應用主機。當WebHostBuilder被建立時,可以在環境變數中找到ASPNETCORE_為字首的主機配置。

(1).Connection string prefixes

Configuration API(配置api)對於四個連線字串環境變數有特殊的處理規則。如果沒有字首作用到AddEnvironmentVariables,帶有下面字首的環境變數會被載入到應用中

當一個環境變數被發現,並且帶有上面四個之一的字首被載入到配置:

  • configuration key通過移除環境變數字首來建立,並且增加一個configuration key section(ConnectionStrings)
  • 一個新的configuration key-value pair被建立了,它代表database connection provider。(只有CUSTOMCONNSTR_,which has no stated provider).

 

10.File Configuration Provider

FileConfigurationProvider是從檔案系統中載入配置的基礎類。下面的configuration providers是作用特定的檔案型別:

  • INI Configuration Provider
  • JSON Configuration Provider
  • XML Configuration Provider

INI Configuration Provider

IniConfigurationProvider從INI檔案中載入配置。

要啟用INI 檔案配置,在ConfigurationBuilder例項中呼叫AddIniFile擴充套件方法。

重寫一些指定配置選項:

  • 檔案是否是可選的
  • 如果檔案改變,配置是否重新載入
  • IFileProvider用於獲取檔案

呼叫ConfigureAppConfiguration,當建立host指定應用配置時:

public class Program
{
    public static void Main(string[] args)
    {
        CreateWebHostBuilder(args).Build().Run();
    }

    public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
        WebHost.CreateDefaultBuilder(args)
            .ConfigureAppConfiguration((hostingContext, config) =>
            {
                config.SetBasePath(Directory.GetCurrentDirectory());
                config.AddIniFile(
                    "config.ini", optional: true, reloadOnChange: true);
            })
            .UseStartup<Startup>();
}

基礎路徑(base path)是通過SetBasePath設定。SetBasePath是在Microsoft.Extensions.Configuration.FileExtension包中,這個包是在Microsoft.AspNetCore.App metapackage.

當直接建立一個WebHostBuilder,呼叫UseConfiguration:

var config = new ConfigurationBuilder()
    .SetBasePath(Directory.GetCurrentDirectory())
    .AddIniFile("config.ini", optional: true, reloadOnChange: true)
    .Build();

var host = new WebHostBuilder()
    .UseConfiguration(config)
    .UseKestrel()
    .UseStartup<Startup>();

一個INI configuration file示例:

[section0]
key0=value
key1=value

[section1]
subsection:key=value

[section2:subsection0]
key=value

[section2:subsection1]
key=value

上面的配置檔案載入下面的key和value:

  • section0:key0
  • section0:key1
  • section1:subsection:key
  • section2:subsection0:key
  • section2:subsection1:key

JSON Configuration Provider

JsonConfigurationProvider從JSON檔案中載入配置.

要啟用JSON檔案配置,在ConfigurationBuilder例項上,呼叫AddJsonFile擴充套件方法。

重寫一些指定配置選項:

  • 檔案是否是可選的
  • 如果檔案改變,配置是否重新載入
  • IFileProvider用於獲取檔案

AddJsonFile會自動呼叫兩次,當你用CreateDefaultBuilder初始化一個WebHostBuilder時。這個方法被呼叫載入配置,從下面的:

  • appsettings.json - 這個檔案被首先讀取。這個檔案的環境版本可以重寫appsettings.json檔案提供的值。
  • appsettings.{Environment}.json - 這個檔案的環境版本(environment version)依據於IHostingEnvironment.EnvironmentName被載入

呼叫ConfiureAppConfiguration,當建立host來指定應用配置,通過其他檔案而不是通過appsettings.json和appsettings.{Environment}.json檔案。

public class Program
{
    public static void Main(string[] args)
    {
        CreateWebHostBuilder(args).Build().Run();
    }

    public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
        WebHost.CreateDefaultBuilder(args)
            .ConfigureAppConfiguration((hostingContext, config) =>
            {
                config.SetBasePath(Directory.GetCurrentDirectory());
                config.AddJsonFile(
                    "config.json", optional: true, reloadOnChange: true);
            })
            .UseStartup<Startup>();
}

當直接建立WebHostBuilder,呼叫UseConfiguration:

var config = new ConfigurationBuilder()
    .SetBasePath(Directory.GetCurrentDirectory())
    .AddJsonFile("config.json", optional: true, reloadOnChange: true)
    .Build();

var host = new WebHostBuilder()
    .UseConfiguration(config)
    .UseKestrel()
    .UseStartup<Startup>();

Example

示例應用利用靜態方法CreateDefaultBuilder來建立host,它包含兩次對AddJsonFile的呼叫。配置從appsettings.json和appsettings.{Environment}.json檔案中載入

  1. 執行示例應用。瀏覽 http://localhost:5000
  2. 觀察配置中的輸出

XML Configuration Provider

XmlConfigurationProvider從XML檔案中載入配置。

要啟用XML檔案配置,在ConfigurationBuilder例項上呼叫AddXmlFile擴充套件方法。

配置檔案的根節點被忽略了,當配置key-value pairs被建立了。不要在檔案中指定一個Document Type Definition(DTD)或者名稱空間。

呼叫ConfigureAppConfiguration,當建立host來指定應用的配置時:

public class Program
{
    public static void Main(string[] args)
    {
        CreateWebHostBuilder(args).Build().Run();
    }

    public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
        WebHost.CreateDefaultBuilder(args)
            .ConfigureAppConfiguration((hostingContext, config) =>
            {
                config.SetBasePath(Directory.GetCurrentDirectory());
                config.AddXmlFile(
                    "config.xml", optional: true, reloadOnChange: true);
            })
            .UseStartup<Startup>();
}

當直接建立WebHostBuilder,呼叫UseConfiguration:

var config = new ConfigurationBuilder()
    .SetBasePath(Directory.GetCurrentDirectory())
    .AddXmlFile("config.xml", optional: true, reloadOnChange: true)
    .Build();

var host = new WebHostBuilder()
    .UseConfiguration(config)
    .UseKestrel()
    .UseStartup<Startup>();

(1).XML配置檔案可以使用distinct元素名稱對於repeating sections:

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
  <section0>
    <key0>value</key0>
    <key1>value</key1>
  </section0>
  <section1>
    <key0>value</key0>
    <key1>value</key1>
  </section1>
</configuration>

前面的配置檔案在家下面的key和value:

  • section0:key0
  • section0:key1
  • section1:key0
  • section1:key1

(2).還可以如下,用name屬性區分元素的形式:

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
  <section name="section0">
    <key name="key0">value</key>
    <key name="key1">value</key>
  </section>
  <section name="section1">
    <key name="key0">value</key>
    <key name="key1">value</key>
  </section>
</configuration>

之前的配合檔案載入下面的key和value:

  • section:section0:key:key0
  • section:section0:key:key1
  • section:section1:key:key0
  • section:section1:key:key1

(3).屬性也可以用於值上:

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
  <key attribute="value" />
  <section>
    <key attribute="value" />
  </section>
</configuration>

之前的配置檔案載入下面的key和value:

  • key:attribute
  • section:key:attribute

11.Key-per-file Configuration Provider

KeyPerFileConfigurationProvider使用一個目錄檔案作為configuration key-value pairs.其中key是檔名。 value包含檔案內容。Key-per-file Configuration Provider用於Docker hosting 場景。

要啟用key-per-file configuration, 在ConfigurationBuilder例項上,呼叫AddKeyPerFile擴充套件方法。檔案的目錄路徑(directoryPath)必須是絕對路徑。

Overloads permit specifying:

  • Action<KeyPerFileConfigurationSource>委託 ,來配置source
  • 目錄是否是可選的,和目錄路徑

在檔名字中,雙下劃線(__)用作一個configuration key 分隔符。例如,檔名Logging__LogLevel__System產生configuration key : Logging:logLevel:System

呼叫ConfigureAppConfiguration,當建立host來指定應用的配置時:

public class Program
{
    public static void Main(string[] args)
    {
        CreateWebHostBuilder(args).Build().Run();
    }

    public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
        WebHost.CreateDefaultBuilder(args)
            .ConfigureAppConfiguration((hostingContext, config) =>
            {
                config.SetBasePath(Directory.GetCurrentDirectory());
                var path = Path.Combine(
                    Directory.GetCurrentDirectory(), "path/to/files");
                config.AddKeyPerFile(directoryPath: path, optional: true);
            })
            .UseStartup<Startup>();
}

當直接建立WebHostBuilder時,呼叫UseConfiguration:

var path = Path.Combine(Directory.GetCurrentDirectory(), "path/to/files");
var config = new ConfigurationBuilder()
    .AddKeyPerFile(directoryPath: path, optional: true)
    .Build();

var host = new WebHostBuilder()
    .UseConfiguration(config)
    .UseKestrel()
    .UseStartup<Startup>();

12.Memory Configuration Provider

MemoryConfigurationProvider使用記憶體集合作為配置key-value pairs.

要使用in-memory collection configuration,在ConfigurationBuilder例項上,呼叫AddInMemoryCollection擴充套件方法。

The configuration provider可以使用IEnumerable<KeyValuePair<String,String>>來初始化。

當建立host來指定應用配置時,呼叫ConfigureAppConfiguration:

public class Program
{
    public static readonly Dictionary<string, string> _dict = 
        new Dictionary<string, string>
        {
            {"MemoryCollectionKey1", "value1"},
            {"MemoryCollectionKey2", "value2"}
        };

    public static void Main(string[] args)
    {
        CreateWebHostBuilder(args).Build().Run();
    }

    public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
        WebHost.CreateDefaultBuilder(args)
            .ConfigureAppConfiguration((hostingContext, config) =>
            {
                config.AddInMemoryCollection(_dict);
            })
            .UseStartup<Startup>();
}

當直接建立WebHostBuilder時,呼叫UseConfiguration:

var dict = new Dictionary<string, string>
    {
        {"MemoryCollectionKey1", "value1"},
        {"MemoryCollectionKey2", "value2"}
    };

var config = new ConfigurationBuilder()
    .AddInMemoryCollection(dict)
    .Build();

var host = new WebHostBuilder()
    .UseConfiguration(config)
    .UseKestrel()
    .UseStartup<Startup>();

三.其他

13.GetValue

ConfigurationBinder.GetValue<T> 從配置檔案中用特定的key提出value,並且轉化為特定型別。如果這個key沒找到,可以提供預設值。

下面的例子:

  • 用key為NumberKey,從配置中提取string value.如果NumberKey沒有在配置中找到,預設的value值99會被使用
  • 把這個value轉化為int型別
  • 通過page儲存值
public class IndexModel : PageModel
{
    public IndexModel(IConfiguration config)
    {
        _config = config;
    }

    public int NumberConfig { get; private set; }

    public void OnGet()
    {
        NumberConfig = _config.GetValue<int>("NumberKey", 99);
    }
}

14.GetSection, GetChildren, and Exists

{
  "section0": {
    "key0": "value",
    "key1": "value"
  },
  "section1": {
    "key0": "value",
    "key1": "value"
  },
  "section2": {
    "subsection0" : {
      "key0": "value",
      "key1": "value"
    },
    "subsection1" : {
      "key0": "value",
      "key1": "value"
    }
  }
}

當檔案被讀取到配置中,如下:

  • section0:key0
  • section0:key1
  • section1:key0
  • section1:key1
  • section2:subsection0:key0
  • section2:subsection0:key1
  • section2:subsection1:key0
  • section2:subsection1:key1

GetSection

IConfiguration.GetSection用特定的subsection key提取一個configuration subsection.

要返回一個包含在section1中的key-value pairs的IConfigurationSection,呼叫GetSection並且應用section name:

var configSection = _config.GetSection("section1");

其中configSection沒有value,只有一個key和path.

相似的,要包含key為section2:subsection0的value,呼叫GetSection並且應用section path:

var configSection = _config.GetSection("section2:subsection0");

GetSection不會返回null。 如果沒有匹配的section,一個空的IConfigurationSection會被返回。

 GetChildren

呼叫在section2上的IConfiguration.GetChildren包含:

  • subsection0
  • subsection1
var configSection = _config.GetSection("section2");
var children = configSection.GetChildren();

Exists

用ConfigurationExtensions.Exists來確定configuration section是否存在:

var sectionExists = _config.GetSection("section2:subsection2").Exists();

給出的例子中的資料,sectionExists是false,因為section2:subsection2這個section不存在。

15.Bind to a class

配置可以被繫結到類中。

示例包含一個Startship的model(Models/Starship.cs):

public class Starship
{
    public string Name { get; set; }
    public string Registry { get; set; }
    public string Class { get; set; }
    public decimal Length { get; set; }
    public bool Commissioned { get; set; }
}

Starship.json檔案中starship的section節建立了配置,當示例應用使用JSON Configuration Provider載入配置時。

{
  "starship": {
    "name": "USS Enterprise",
    "registry": "NCC-1701",
    "class": "Constitution",
    "length": 304.8,
    "commissioned": false
  },
  "trademark": "Paramount Pictures Corp. http://www.paramount.com"
}

下面的配置被建立了:

示例應用利用key為starship來呼叫GetSection。Starship key-value pairs被分離。在繫結例項值後:

var starship = new Starship();
_config.GetSection("starship").Bind(starship);
Starship = starship;

16.Bind to an object graph

示例繫結一個TvShow的model,它包含Metadata和Actors類:

public class TvShow
{
    public Metadata Metadata { get; set; }
    public Actors Actors { get; set; }
    public string Legal { get; set; }
}

public class Metadata
{
    public string Series { get; set; }
    public string Title { get; set; }
    public DateTime AirDate { get; set; }
    public int Episodes { get; set; }
}

public class Actors
{
    public string Names { get; set; }
}

示例應用有個tvshow.xml檔案包含配置資料:

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
  <tvshow>
    <metadata>
      <series>Dr. Who</series>
      <title>The Sun Makers</title>
      <airdate>11/26/1977</airdate>
      <episodes>4</episodes>
    </metadata>
    <actors>
      <names>Tom Baker, Louise Jameson, John Leeson</names>
    </actors>
    <legal>(c)1977 BBC https://www.bbc.co.uk/programmes/b006q2x0</legal>
  </tvshow>
</configuration>

配置被Bind方法繫結到了TvShow物件上。如下:

var tvShow = new TvShow();
_config.GetSection("tvshow").Bind(tvShow);
TvShow = tvShow;

ConfigurationBinder.Get<T>繫結和返回指定的型別。Get<T>比使用Bind更方便。下面的程式碼展示了怎麼再之前的程式碼使用Get<T>,如下:

TvShow = _config.GetSection("tvshow").Get<TvShow>();

17.Bind an array to a c lass

Bind還支援繫結陣列.

In-memory array processing

考慮下面配置中的keys和values:

 

示例應用中的keys和values被使用Memory Configuration Provider被載入:

public class Program
{
    public static Dictionary<string, string> arrayDict = 
        new Dictionary<string, string>
        {
            {"array:entries:0", "value0"},
            {"array:entries:1", "value1"},
            {"array:entries:2", "value2"},
            {"array:entries:4", "value4"},
            {"array:entries:5", "value5"}
        };

    public static void Main(string[] args)
    {
        CreateWebHostBuilder(args).Build().Run();
    }

    public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
        WebHost.CreateDefaultBuilder(args)
            .ConfigureAppConfiguration((hostingContext, config) =>
            {
                config.SetBasePath(Directory.GetCurrentDirectory());
                config.AddInMemoryCollection(arrayDict);
                config.AddJsonFile(
                    "json_array.json", optional: false, reloadOnChange: false);
                config.AddJsonFile(
                    "starship.json", optional: false, reloadOnChange: false);
                config.AddXmlFile(
                    "tvshow.xml", optional: false, reloadOnChange: false);
                config.AddEFConfiguration(
                    options => options.UseInMemoryDatabase("InMemoryDb"));
                config.AddCommandLine(args);
            })
            .UseStartup<Startup>();
}

陣列跳過了索引為3的值。Configuration binder不能繫結null 值或者建立null entries在繫結物件中。

在示例中,一個類用來繫結配置資料:

public class ArrayExample
{
    public string[] Entries { get; set; }
}

配置資料被繫結到物件上:

var arrayExample = new ArrayExample();
_config.GetSection("array").Bind(arrayExample);

也可以使用ConfigurationBinder.Get<T>:

ArrayExample = _config.GetSection("array").Get<ArrayExample>();

ArrayExample的例項,接收到配置中的陣列資料:

在繫結物件中,索引為3有的是array:4的值。

要處理這樣的情況,可以這樣,使用一個額外的JOSN Configuration Provider處理丟失的key-value pair, ArrayExample.Entries匹配完整的配置陣列:

missing_value.json:

{
  "array:entries:3": "value3"
}

在ConfigureAppConfiguration中:

config.AddJsonFile("missing_value.json", optional: false, reloadOnChange:false);

下面的值會被載入到配置中:

如果ArrayExample類的例項是在JSON Configuration Provider包含索引3後被繫結,ArrayExample.Entries陣列會包含這個值:

JSON array processing

如果JSON檔案包含一個數組,配置keys被建立為陣列的索引。下面的配置檔案中,subsection是一個數組:

{
  "json_array": {
    "key": "valueA",
    "subsection": [
      "valueB",
      "valueC",
      "valueD"
    ]
  }
}

JSON Configuration Provider讀取配置資料到下面所列:

在示例應用中,下面的類用來繫結配置資料:

public class JsonArrayExample
{
    public string Key { get; set; }
    public string[] Subsection { get; set; }
}

在繫結後,JsonArrayExample.Key有值valueA,subsection的值儲存在Subsection屬性的陣列中。

四.對於自定義configuration provider

18.Custom configuration provider

示例應用說明怎麼建立一個基礎的configuration provider,它可以使用Entity Framework從資料庫讀取資料。

provider有下面特徵:

  • EF in-memory database是用於證明目的。
  • 在startup中,provider讀取資料庫表到配置中.
  • Reload-on-change沒有實現,所以在應用啟動後更新資料庫對於應用的配置沒有作用。

定義個EFConfigurationValue實體來儲存資料庫中的配置資料。

Models/EFConfigurationValues.cs:

public class EFConfigurationValue
{
    public string Id { get; set; }
    public string Value { get; set; }
}

增加一個EFConfigurationContext來儲存和取得配置值。

EFConfigurationProvider/EFConfigurationContext.cs:

public class EFConfigurationContext : DbContext
{
    public EFConfigurationContext(DbContextOptions options) : base(options)
    {
    }

    public DbSet<EFConfigurationValue> Values { get; set; }
}

建立一個類實現IConfigurationSource.

EFConfigurationProvider/EFConfigurationSource.cs:

public class EFConfigurationSource : IConfigurationSource
{
    private readonly Action<DbContextOptionsBuilder> _optionsAction;

    public EFConfigurationSource(Action<DbContextOptionsBuilder> optionsAction)
    {
        _optionsAction = optionsAction;
    }

    public IConfigurationProvider Build(IConfigurationBuilder builder)
    {
        return new EFConfigurationProvider(_optionsAction);
    }
}

建立一個自定義的configuration provider通過繼承ConfigurationProvider.這個configuration provider初始化資料庫,當它是空的時。

EFConfigurationProvider/EFConfigurationProvider.cs:

public class EFConfigurationProvider : ConfigurationProvider
{
    public EFConfigurationProvider(Action<DbContextOptionsBuilder> optionsAction)
    {
        OptionsAction = optionsAction;
    }

    Action<DbContextOptionsBuilder> OptionsAction { get; }

    // Load config data from EF DB.
    public override void Load()
    {
        var builder = new DbContextOptionsBuilder<EFConfigurationContext>();

        OptionsAction(builder);

        using (var dbContext = new EFConfigurationContext(builder.Options))
        {
            dbContext.Database.EnsureCreated();

            Data = !dbContext.Values.Any()
                ? CreateAndSaveDefaultValues(dbContext)
                : dbContext.Values.ToDictionary(c => c.Id, c => c.Value);
        }
    }

    private static IDictionary<string, string> CreateAndSaveDefaultValues(
        EFConfigurationContext dbContext)
    {
        // Quotes (c)2005 Universal Pictures: Serenity
        // https://www.uphe.com/movies/serenity
        var configValues = new Dictionary<string, string>
            {
                { "quote1", "I aim to misbehave." },
                { "quote2", "I swallowed a bug." },
                { "quote3", "You can't stop the signal, Mal." }
            };

        dbContext.Values.AddRange(configValues
            .Select(kvp => new EFConfigurationValue 
                {
                    Id = kvp.Key,
                    Value = kvp.Value
                })
            .ToArray());

        dbContext.SaveChanges();

        return configValues;
    }
}

一個AddEFConfiguration擴充套件方法允許增加配置源到一個ConfigurationBuilder.

Extensions/EntityFrameworkExtension.cs:

public static class EntityFrameworkExtensions
{
    public static IConfigurationBuilder AddEFConfiguration(
        this IConfigurationBuilder builder, 
        Action<DbContextOptionsBuilder> optionsAction)
    {
        return builder.Add(new EFConfigurationSource(optionsAction));
    }
}

下面的程式碼展示了怎麼在Program.cs中使用自定義的EFConfigurationProvider:

 

public class Program
{
    public static Dictionary<string, string> arrayDict = 
        new Dictionary<string, string>
        {
            {"array:entries:0", "value0"},
            {"array:entries:1", "value1"},
            {"array:entries:2", "value2"},
            {"array:entries:4", "value4"},
            {"array:entries:5", "value5"}
        };

    public static void Main(string[] args)
    {
        CreateWebHostBuilder(args).Build().Run();
    }

    public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
        WebHost.CreateDefaultBuilder(args)
            .ConfigureAppConfiguration((hostingContext, config) =>
            {
                config.SetBasePath(Directory.GetCurrentDirectory());
                config.AddInMemoryCollection(arrayDict);
                config.AddJsonFile(
                    "json_array.json", optional: false, reloadOnChange: false);
                config.AddJsonFile(
                    "starship.json", optional: false, reloadOnChange: false);
                config.AddXmlFile(
                    "tvshow.xml", optional: false, reloadOnChange: false);
                config.AddEFConfiguration(
                    options => options.UseInMemoryDatabase("InMemoryDb"));
                config.AddCommandLine(args);
            })
            .UseStartup<Startup>();
}

 

19.Access configuration during startup

在Startup.ConfigureServices中注入IConfiguration到Startup建構函式中來取得配置資料。要再Startup.Configure中取得配置資料,要麼直接注入IConfiguration到方法中,要麼使用建構函式中的例項:

public class Startup
{
    private readonly IConfiguration _config;

    public Startup(IConfiguration config)
    {
        _config = config;
    }

    public void ConfigureServices(IServiceCollection services)
    {
        var value = _config["key"];
    }

    public void Configure(IApplicationBuilder app, IConfiguration config)
    {
        var value = config["key"];
    }
}

20.Access configuration in a Razor Pages page or MVC view

在Razor Pages page或者MVC view中取得configuration settings.增加一個using指令為Microsoft.Extensions.Configuration 名稱空間,並且把IConfiguration注入到page或者view中。

在Razor Pages page:

@page
@model IndexModel
@using Microsoft.Extensions.Configuration
@inject IConfiguration Configuration

<!DOCTYPE html>
<html lang="en">
<head>
    <title>Index Page</title>
</head>
<body>
    <h1>Access configuration in a Razor Pages page</h1>
    <p>Configuration value for 'key': @Configuration["key"]</p>
</body>
</html>

在MVC view中:

@using Microsoft.Extensions.Configuration
@inject IConfiguration Configuration

<!DOCTYPE html>
<html lang="en">
<head>
    <title>Index View</title>
</head>
<body>
    <h1>Access configuration in an MVC view</h1>
    <p>Configuration value for 'key': @Configuration["key"]</p>
</body>
</html>

 

參考網址:

https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/?view=aspnetcore