ASP.NET Core 基本知识 - 配置(Configuration)

使用 dotnet new 或者 Visual Studio 创建的 ASP.NET Core web 应用程序生成以下代码:

复制代码
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}

public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
        .ConfigureWebHostDefaults(webBuilder =>
        {
            webBuilder.UseStartup<Startup>();
        });

}
复制代码
CreateDefaultBuilder 按照以下顺序为应用程序提供默认配置:

ChainedConfigurationProvider:添加一个现存的 IConfiguration 作为一个源。在一个默认配置例子中,添加主机(host)配置并且设置它作为第一个应用程序配置的源。
appsettings.json 使用 JSON 配置提供器(JSON configuration provider)。
appsetttings.Environment.json 使用 JSON 配置提供器(JSON configuration provider)。例如,appsettings.Production.json 和 appsettings.Developments.json。
App secrets,当应用程序运行在开发 (Development) 环境中。
环境变量使用 Environment Variables configuration provider。
命令行参数使用 Command-line configuration provider。
后添加的配置提供器会覆盖先前添加的键值设置。例如,如果 MyKey 同事在 appsettings.json 和 环境变量中设置,环境变量的值将会被使用。如果使用默认的配置提供器,命令行配置提供器(Command-line configuration provider) 将会覆盖所有的提供器。

关于 CreateDefaultBuilder 的更多信息,查看 Default builder settings。

下面的代码展示了使能的配置提供器的添加顺序:

复制代码
public class Index2Model : PageModel
{
private IConfigurationRoot ConfigRoot;

public Index2Model(IConfiguration configRoot)
{
    ConfigRoot = (IConfigurationRoot)configRoot;
}

public ContentResult OnGet()
{           
    string str = "";
    foreach (var provider in ConfigRoot.Providers.ToList())
    {
        str += provider.ToString() + "\n";
    }

    return Content(str);
}

}
复制代码
appsettings.json

考虑下面的 appsettings.json 文件:

复制代码
{
“Position”: {
“Title”: “Editor”,
“Name”: “Joe Smith”
},
“MyKey”: “My appsettings.json Value”,
“Logging”: {
“LogLevel”: {
“Default”: “Information”,
“Microsoft”: “Warning”,
“Microsoft.Hosting.Lifetime”: “Information”
}
},
“AllowedHosts”: “*”
}
复制代码
示例 (sample download) 中的代码展示了几个上面的配置设置:

复制代码
public class TestModel : PageModel
{
// requires using Microsoft.Extensions.Configuration;
private readonly IConfiguration Configuration;

public TestModel(IConfiguration configuration)
{
    Configuration = configuration;
}

public ContentResult OnGet()
{
    var myKeyValue = Configuration["MyKey"];
    var title = Configuration["Position:Title"];
    var name = Configuration["Position:Name"];
    var defaultLogLevel = Configuration["Logging:LogLevel:Default"];


    return Content($"MyKey value: {myKeyValue} \n" +
                   $"Title: {title} \n" +
                   $"Name: {name} \n" +
                   $"Default Log Level: {defaultLogLevel}");
}

}
复制代码
默认的 JsonConfigurationProvider 按照以下顺序加载配置:

  1. appsettings.json

  2. appsettings.Environment.json:例如,appsettings.Production.json 和 appsettings.Development.json 文件。文件的环境版本基于 IHostingEnvironment.EnvironmentName,更多信息,查看 Use multiple environments in ASP.NET Core.

appsettings.Environment.json 中的值会覆盖 appsettings.json 中的值。例如,默认的:

在开发环境中,appsettings.Development.json 配置会覆盖 appsettings.json 中存在的值
在生产环境中,appsettings.Production.json 的配置会覆盖 appsettings.json 中存在的值,例如,当部署应用程序到 Azure 上的时候。
使用选项模型绑定继承配置数据
读取相关配置值的推荐方式是使用选项模型 (options pattern)。例如,读取下面的配置值:

“Position”: {
“Title”: “Editor”,
“Name”: “Joe Smith”
}
创建 PositionOptions 类:

复制代码
public class PositionOptions
{
public const string Position = “Position”;

public string Title { get; set; }
public string Name { get; set; }

}
复制代码
一个选项类:

必须是带有公共无参数的构造方法的非抽象类
所有公共的可读写的属性类型要被绑定
字段没有绑定。在前面的代码中,Position 没有绑定。使用 Position 属性,因此字符串 “Position” 就不用在绑定类到一个配置提供器的时候硬编码在应用程序中
下面的代码:

调用 ConfigurationBinder.Bind 绑定 PositionOptions 类到 Position 区域
展示 Position 配置数据
复制代码
public class Test22Model : PageModel
{
private readonly IConfiguration Configuration;

public Test22Model(IConfiguration configuration)
{
    Configuration = configuration;
}

public ContentResult OnGet()
{
    var positionOptions = new PositionOptions();
    Configuration.GetSection(PositionOptions.Position).Bind(positionOptions);

    return Content($"Title: {positionOptions.Title} \n" +
                   $"Name: {positionOptions.Name}");
}

}
复制代码
在上面的代码中,默认的,在应用程序启动后对于 JSON 配置文件的改变也会被读取到。

ConfigurationBinder.Get 绑定和返回指定的类型。ConfigurationBinder.Get 可能比使用 ConfigurationBinder.Bind 更方便。下面的代码展示了如何使用 ConfigurationBinder.Get 使用 PositionOptions 类:

复制代码
public class Test21Model : PageModel
{
private readonly IConfiguration Configuration;
public PositionOptions positionOptions { get; private set; }

public Test21Model(IConfiguration configuration)
{
    Configuration = configuration;
}

public ContentResult OnGet()
{            
    positionOptions = Configuration.GetSection(PositionOptions.Position)
                                                 .Get<PositionOptions>();

    return Content($"Title: {positionOptions.Title} \n" +
                   $"Name: {positionOptions.Name}");
}

}
复制代码
默认的,上面的代码会读取到在应用程序启动后对于 JSON 配置文件的更改。

使用 options patter 的一种方法是绑定 Position 区域并且添加到依赖注入服务容器 (dependency injection service container) 中。下面的代码中,PositionOptions 在 Configure 中被添加到服务容器中,并绑定到配置:

复制代码
public void ConfigureServices(IServiceCollection services)
{
services.Configure(Configuration.GetSection(
PositionOptions.Position));
services.AddRazorPages();
}
复制代码
使用了上面的代码,下面的代码读取 position options:

复制代码
public class Test2Model : PageModel
{
private readonly PositionOptions _options;

public Test2Model(IOptions<PositionOptions> options)
{
    _options = options.Value;
}

public ContentResult OnGet()
{
    return Content($"Title: {_options.Title} \n" +
                   $"Name: {_options.Name}");
}

}
复制代码
上面的代码在应用程序启动后不会读取到对 JSON 配置文件的更改。如果要读取应用程序启动后的更改,可以使用 IOptionsSnapshot。

使用默认(default)的配置,appsettings.json 和 appsettings.Environment.json 文件使能了 reloadOnChange: true 。在应用程序启动后对 appsettings.json 和 appsettings.Environment.json 的更改会被 JSON configuration provider 读取到。

查看本文档中 JSON configuration provider 关于添加更多 JSON 配置文件的信息。

合并服务集合
考虑下面的 ConfigureServices 方法,其中注册服务和配置选项:

复制代码
public void ConfigureServices(IServiceCollection services)
{
services.Configure(
Configuration.GetSection(PositionOptions.Position));
services.Configure(
Configuration.GetSection(ColorOptions.Color));

services.AddScoped<IMyDependency, MyDependency>();
services.AddScoped<IMyDependency2, MyDependency2>();

services.AddRazorPages();

}
复制代码
相关的一组的注册可以被移动到扩展方法中去注册服务。例如,配置服务被添加到下面的类中:

复制代码
using ConfigSample.Options;
using Microsoft.Extensions.Configuration;

namespace Microsoft.Extensions.DependencyInjection
{
public static class MyConfigServiceCollectionExtensions
{
public static IServiceCollection AddConfig(
this IServiceCollection services, IConfiguration config)
{
services.Configure(
config.GetSection(PositionOptions.Position));
services.Configure(
config.GetSection(ColorOptions.Color));

        return services;
    }
}

}
复制代码
其余的服务在一个相似的类中被注册。下面的 ConfigureServices 方法使用的新的扩展方法注册服务:

复制代码
public void ConfigureServices(IServiceCollection services)
{
services.AddConfig(Configuration)
.AddMyDependencyGroup();

services.AddRazorPages();

}
复制代码
备注:每一个 services.Add{GROUP_NAME} 扩展方法添加和潜在的会配置服务。例如,AddControllersWithViews 添加带有视图的 MVC 控制器的服务,AddRazorPages 添加带有 Razor Pages 的服务。我们推荐应用程序遵守命名的约定。把扩展方法统一放到命名空间 Microsoft.Extensions.DependencyInjection 中去封装一组服务的注册。

安全和用户秘密
数据配置指导:

永远不要把密码或者其他敏感数据使用配置提供器保存在代码中或者保存在文本配置文件中。在开发过程中,可以使用 Secret Manager 工具保存秘密数据
不要在开发环境或者测试环境中使用生产环境的秘密数据
在工程外部指定秘密数据,以防被意外的提交到源代码仓库中
默认的,用户秘密配置源是在 JSON 配置源之后注册的。因此,用户秘密的键值会生效,而不是 appsettings.json 和 appsettings.Environment.json 中的键值。

更多关于存储密码或者其他敏感数据的信息:

Use multiple environments in ASP.NET Core
Safe storage of app secrets in development in ASP.NET Core:包含关于使用环境变量存储敏感数据的建议。秘密管理工具使用 File configuration provider 在本地系统上保存用户密码在一个 JSON 文件
Azure Key Vault 为 ASP.NET Core 应用程序安全的存储应用程序的秘密。更多信息查看 Azure Key Vault Configuration Provider in ASP.NET Core。

环境变量
使用默认的配置,EnvironmentVariablesConfigurationProvider 在读取 appsettings.json,appsettings.Environment.json,和 user secrets 之后从环境变量加载键值对。因此,从环境变量读取到的键值会覆盖从 appsettings.json, appsettings.Environment.json 和 user secrets 中读取到的值。

: 分隔符在所有平台上对于环境便令分级键都是不工作的。__ 双下换线:

所有平台都支持。例如, Bash 不支持 : 分隔符,但是支持 __
会自动的被一个 : 替换
下面的设置命令:

在 Windows 上设置前面示例(preceding exampl)中的环境键值和值
使用示例程序(sample download)测试设置。dotnet run 命令必须在工程目录中运行
set MyKey=“My key from Environment”
set Position__Title=Environment_Editor
set Position__Name=Environment_Rick
dotnet run
上面的环境变量设置:

仅仅在设置他们的命令行窗口中启动的进程中
不会被使用 Visual Studio 启动的浏览器读取
下面的 setx 命令可以被用来在 Windows 上设置环境键和值。不同于 set,setx 是持久化的。/M 在系统环境设置变量。如果没有使用 /M 开关,一个用户的环境变量会被设置。

setx MyKey “My key from setx Environment” /M
setx Position__Title Setx_Environment_Editor /M
setx Position__Name Environment_Rick /M
为了测试上面的命令会覆盖 appsettings.json 和 asppsettings.Environment.json 的配置,需要做以下操作:

使用 Visual Studio: 退出和重启 Visual Studio
使用命令行:启动一个新的命令窗口,输入 dotnet run
调用 AddEnvironmentVariables,使用一个字符串指定环境变量的前缀:

复制代码
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}

public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
        .ConfigureAppConfiguration((hostingContext, config) =>
        {
            config.AddEnvironmentVariables(prefix: "MyCustomPrefix_");
        })
        .ConfigureWebHostDefaults(webBuilder =>
        {
            webBuilder.UseStartup<Startup>();
        });

}
复制代码
在上面的代码中:

config.AddEnvironmentVariables(prefix: “MyCustomPrefix_”) 在默认配置提供器 (default configuration providers) 之后添加。关于配置提供器顺序的示例,查看 JSON 配置提供器 (JSON configuration provider)。
使用 MyCustomPrefix_ 前缀设置的环境变量覆盖了默认配置提供器 (default configuration providers)。这包括没有前缀的环境变量。
当配置键值对被读取的时候,前缀会被去除。

下面的命令测试自定义前缀:

set MyCustomPrefix_MyKey=“My key with MyCustomPrefix_ Environment”
set MyCustomPrefix_Position__Title=Editor_with_customPrefix
set MyCustomPrefix_Position__Name=Environment_Rick_cp
dotnet run
默认配置 (default configuration) 加载带有前缀为 DOTNET_ 和 ASPNETCORE_ 的环境变量和命令行参数。DOTNET_ 和 ASPNETCORE_ 前缀被 ASP.NET Core 用来配置主机和应用程序配置 (host and app configuration),不能用来作为用户配置。更多关于主机和应用程序的配置,查看 .NET Generic Host。

关于 Azure App Service,在设置 (Settings) > 配置 (Configuration) 页面选择 新建应用程序设置 (New application setting)。Azure 应用程序服务设置:

在休息是加密,并通过加密通道传输
暴露为环境变量
更多信息,查看 Azure Apps: Override app configuration using the Azure Portal。

查看 Connection string prefixes 了解关于 Azure 数据库连接字符串的信息。

环境变量命名
环境变量的命名反映了 appsettings.json 文件的结构。层级中的每一个元素使用双下划线(推荐的)或者冒号分割开来。当一个元素的结构包含一个数组的时候,数组的索引应该被当做是当前路径中的一个额外的元素名称。考虑下面的 appsettings.json 文件和它在环境变量中等价的值。

appsettings.json

复制代码
{
“SmtpServer”: “smtp.example.com”,
“Logging”: [
{
“Name”: “ToEmail”,
“Level”: “Critical”,
“Args”: {
“FromAddress”: “MySystem@example.com”,
“ToAddress”: “SRE@example.com”
}
},
{
“Name”: “ToConsole”,
“Level”: “Information”
}
]
}
复制代码
环境变量 (environment variables)

复制代码
setx SmtpServer=smtp.example.com
setx Logging__0__Name=ToEmail
setx Logging__0__Level=Critical
setx Logging__0__Args__FromAddress=MySystem@example.com
setx Logging__0__Args__ToAddress=SRE@example.com
setx Logging__1__Name=ToConsole
setx Logging__1__Level=Information
复制代码
生成的 launchSettings.json 文件中环境变量的设置
在 launchSettings.json 中设置的环境变量会覆盖那些在系统环境中设置的值。例如,ASP.NET Core web 模板会生成一个 lauchSettings.json 文件,文件中设置了

endpoint 配置:

“applicationUrl”: “https://localhost:5001;http://localhost:5000”
对 applicationUrl 的配置设置环境变量 ASPNETCORE_URLS 并覆盖环境中的值。

Escape environment variables on Linux
在 linux 上,URL 环境变量的值必须被 escape 后系统才能够解析它。使用 linux systemd-escape 工具生成 http:–localhost:5001

groot@terminus:~$ systemd-escape http://localhost:5001
http:–localhost:5001
显示环境变量
下面的代码在应用程序启动的时候输出显示了环境变量和对应的值,在调试环境设置的时候非常有用:

复制代码
public static void Main(string[] args)
{
var host = CreateHostBuilder(args).Build();

var config = host.Services.GetRequiredService<IConfiguration>();

foreach (var c in config.AsEnumerable())
{
    Console.WriteLine(c.Key + " = " + c.Value);
}
host.Run();

}
复制代码
命令行
使用默认的配置,CommandLineConfigurationProvider 在下列配置源之后从命令行参数键值对中加载配置:

appsettings.json 和 appsettings.Environment.json 文件
开发环境中的 App secrets
环境变量
默认的,在命令行中配置的值会覆盖其它所有配置提供的值。

命令行参数
下面的命令使用 = 设置键和值:

dotnet run MyKey=“My key from command line” Position:Title=Cmd Position:Name=Cmd_Rick
下面的命令使用 / 设置键值:

dotnet run /MyKey “Using /” /Position:Title=Cmd_ /Position:Name=Cmd_Rick
下面的命令使用 – 设置键值:

dotnet run --MyKey “Using --” --Position:Title=Cmd-- --Position:Name=Cmd–Rick
键值:

必须紧跟 = ,或当值在一个空格后面的时候,键必须有个 – 或者 / 前缀
当使用 = 的时候,值不是必须要有的,例如 MySetting=
在相同的命令行中,不要把使用 = 的键值对和使用空格的键值对的命令行参数混淆。

转换映射
切换映射允许键名替换逻辑。可以给 AddCommandLine 方法提供一个切换替换的字典。

当切换映射字典被用到的时候,字典被用来检查匹配命令行参数提供的键。日过命令行的键在字典中被找到,字典中的值就会被传回用来设置应用程序配置的键值对。切换映射要求任何的命令行键使用一个单独的破折号作为前缀。

切换映射字典键的规则:

切换必须以 - 或者 – 开头
切换映射字典不能包含重复的键

使用一个切换映射字典,需要把它传递给 AddCommandLine 方法:

复制代码
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}

public static IHostBuilder CreateHostBuilder(string[] args)
{
    var switchMappings = new Dictionary<string, string>()
     {
         { "-k1", "key1" },
         { "-k2", "key2" },
         { "--alt3", "key3" },
         { "--alt4", "key4" },
         { "--alt5", "key5" },
         { "--alt6", "key6" },
     };

    return Host.CreateDefaultBuilder(args)
        .ConfigureAppConfiguration((hostingContext, config) =>
        {
            config.AddCommandLine(args, switchMappings);
        })
        .ConfigureWebHostDefaults(webBuilder =>
        {
            webBuilder.UseStartup<Startup>();
        });
}

}
复制代码
下面的代码展示了被替换的键的值:

复制代码
public class Test3Model : PageModel
{
private readonly IConfiguration Config;

public Test3Model(IConfiguration configuration)
{
    Config = configuration;
}

public ContentResult OnGet()
{
    return Content(
            $"Key1: '{Config["Key1"]}'\n" +
            $"Key2: '{Config["Key2"]}'\n" +
            $"Key3: '{Config["Key3"]}'\n" +
            $"Key4: '{Config["Key4"]}'\n" +
            $"Key5: '{Config["Key5"]}'\n" +
            $"Key6: '{Config["Key6"]}'");
}

}
复制代码
下面的命令用来测试键替换:

dotnet run -k1 value1 -k2 value2 --alt3=value2 /alt4=value3 --alt5 value5 /alt6 value6
对于使用切换映射的应用程序,调用 CreateDefaultBuilder 不应该传递参数。CreateDefaultBuilder 方法的 AddCommandLine 的调用不包括映射切换,没有方法可以传递切换映射的字典给 CreateDefaultBuilder。解决方法是允许 ConfigurationBuilder 方法的 AddCommandLine 同时处理参数和切换映射字典而不是传递参数给 CreateDefaultBuilder。

分层配置数据
配置 API 通过使用在配置键中的分界符扁平化分层数据来读取配置数据。

示例程序 (sample download) 包含下面的 appsettings.json 文件:

复制代码
{
“Position”: {
“Title”: “Editor”,
“Name”: “Joe Smith”
},
“MyKey”: “My appsettings.json Value”,
“Logging”: {
“LogLevel”: {
“Default”: “Information”,
“Microsoft”: “Warning”,
“Microsoft.Hosting.Lifetime”: “Information”
}
},
“AllowedHosts”: “*”
}
复制代码
下面示例 (sample download) 中的代码展示了一些配置设置:

复制代码
public class TestModel : PageModel
{
// requires using Microsoft.Extensions.Configuration;
private readonly IConfiguration Configuration;

public TestModel(IConfiguration configuration)
{
    Configuration = configuration;
}

public ContentResult OnGet()
{
    var myKeyValue = Configuration["MyKey"];
    var title = Configuration["Position:Title"];
    var name = Configuration["Position:Name"];
    var defaultLogLevel = Configuration["Logging:LogLevel:Default"];


    return Content($"MyKey value: {myKeyValue} \n" +
                   $"Title: {title} \n" +
                   $"Name: {name} \n" +
                   $"Default Log Level: {defaultLogLevel}");
}

}
复制代码
读取分层配置数据的首选方式是使用选项模型。更多信息,查看本文档中的绑定分层配置数据 (Bind hierarchical configuration data)。

GetSection 和 GetChildren 方法可以用来分离配置数据中的分区和分区中的子部分。这些方法在之后的 GetSection, GetChildren, and Exists 中会描述到。

配置的键和值
配置的键:

不区分大小写。例如 ConnectionString 和 connectionstring 被认为是等价的键
如果一个键和值在多个配置提供器中被设置,最后一个提供器的值将会被使用。更多信息,查看默认配置 (Default configuration)
分层键

  • 在配置 API 内部,一个冒号分隔符 (😃 在所有平台都能工作
  • 在环境变量中,一个冒号分隔符可能不会在所有平台上工作。双下划线 (__) 被所有平台支持,并且会被自动的转换为一个冒号 (😃
  • 在 Azure 键仓库中,分层的键使用 – 作为一个分隔符。当秘密数据被加载到应用程序配置的时候,Azure Key Vault configuration provider 自动使用一个冒号 (😃 替换 (–)。
    ConfigurationBinder 支持绑定数组到在配置键中使用数组索引的对象。数组绑定在 Bind an array to a class 部分描述。
    配置值:

是字符串
Null 值不能存储在配置中或者绑定到对象
配置提供器
下面的表格中显示了 ASP.NET Core 应用程序中可以使用的配置提供器

Provider Providers configuration from
Azure Key Vault configuration provider Azure Key Valut
Azure App configuration provider Azure App Configuration
Command-line configuration provider Command-line parameters
Custom configuration provider Custom source
Environment Variables configuration provider Environment variables
File configuration provider INI,JSON,XML 文件
Key-per-file configuration provider 字典文件
Memory configuration provider 内存中的集合
User secrets 用户配置目录中的文件
配置源的读取的顺序按照它们的配置提供器被指定的顺序。在代码中对配置提供器排序来满足应用程序要求的配置源的顺序。

一个典型的配置提供器的顺序是:

appsettings.json
appsettings.Environment.json
User secrets
使用 Environment Variables configuration provider 的环境变量
使用 Command-line configuration provider 的命令行参数
一个常用的实践是在一系列的配置提供器的最后添加命令行配置提供器,用来覆盖其它提供器的配置设置

上面的提供器的顺序在默认配置 (default configuration) 中使用。

连接字符串前缀
配置 API 对于四种连接字符串环境变量有特殊的处理规则。这些连接字符串会根据应用程序环境解析来配置 Azure 连接字符串。下面表格中的带有前缀的环境变量在应用程序使用默认配置 (default configuration)或者没有前缀没有应用到 AddEnvironmentVariables 的情况下会被加载到应用程序中。

Connection string prefix Provider
CUSTOMCONNSTR_ 自定义提供器
MYSQLCONNSTR_ MySQL
SQLAZURECONNSTR_ Azure SQL Database
SQLCONNSTR_ SQL Server
当一个环境变量被发现并且使用表格中四种前缀的任一种被加载到配置中的时候:

通过移除环境变量的前缀创建配置的键,添加一个配置键的区域(ConnectionStrings)
一个新的配置的键值对被创建,这个键值对代表了数据库连接提供器 (CUSTOMCONNSTR_ 除外,由于没有固定的提供器)
环境变量键 转换后的配置键 提供器配置入口
CUSTOMCONNSTR_{KEY} ConnectionStrings:{KEY} 配置入口没有创建
MYSQLCONNSTR_{KEY} ConnectionString:{KEY}
Key:ConnectionStrings:

{KEY}_ProviderName:

Value:MySql.DataMySqlClient

SQLAZURECONNSTR_{KEY} ConnectionStrings:{KEY}
Key:ConnectionStrings:

{KEY}_ProviderName:

Value:System.Data.SqlClient

SQLCONNSTR_{KEY} ConnectionStrings:{KEY}
Key:ConnectionStrings:

{KEY}_ProviderName:

Value:System.Data.SqlClient

文件配置提供器
FileConfigurationProvider 是从文件系统加载配置的基类。下面的配置提供器都从 FileConfigurationProvider 类继承而来。

INI configuration provider
JSON configuration provider
XML configuration provider
INI 配置提供器
IniConfigurationProvider 在运行时从 INI 文件中加载键值对的配置。

下面的代码清空了所有配置提供器,添加了一些配置提供器:

复制代码
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}

public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
        .ConfigureAppConfiguration((hostingContext, config) =>
        {
            config.Sources.Clear();

            var env = hostingContext.HostingEnvironment;

            config.AddIniFile("MyIniConfig.ini", optional: true, reloadOnChange: true)
                  .AddIniFile($"MyIniConfig.{env.EnvironmentName}.ini",
                                 optional: true, reloadOnChange: true);

            config.AddEnvironmentVariables();

            if (args != null)
            {
                config.AddCommandLine(args);
            }
        })
        .ConfigureWebHostDefaults(webBuilder =>
        {
            webBuilder.UseStartup<Startup>();
        });

}
复制代码
在上面的代码中,文件 MyIniCofig.ini 和 MyIniConfig.Environment.ini 中的配置会被以下配置覆盖:

Environment variables configuration provider
Command-line configuration provider.
示例程序(sample download)包含以下 MyIniConfig.ini 文件:

复制代码
MyKey=“MyIniConfig.ini Value”

[Position]
Title=“My INI Config title”
Name=“My INI Config name”

[Logging:LogLevel]
Default=Information
Microsoft=Warning
复制代码
下面的代码来自示例程序(sample download),展示了前面配置设置:

复制代码
public class TestModel : PageModel
{
// requires using Microsoft.Extensions.Configuration;
private readonly IConfiguration Configuration;

public TestModel(IConfiguration configuration)
{
    Configuration = configuration;
}

public ContentResult OnGet()
{
    var myKeyValue = Configuration["MyKey"];
    var title = Configuration["Position:Title"];
    var name = Configuration["Position:Name"];
    var defaultLogLevel = Configuration["Logging:LogLevel:Default"];


    return Content($"MyKey value: {myKeyValue} \n" +
                   $"Title: {title} \n" +
                   $"Name: {name} \n" +
                   $"Default Log Level: {defaultLogLevel}");
}

}
复制代码
JSON 配置提供器
JsonConfigurationProvider 从 JSON 文件中加载键值对

重载可以指定:

文件是否可选
文件改变配置是否重新加载
考虑下面的代码:

复制代码
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}

public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
        .ConfigureAppConfiguration((hostingContext, config) =>
        {
            config.AddJsonFile("MyConfig.json", 
                optional: true, 
                reloadOnChange: true);
        })
        .ConfigureWebHostDefaults(webBuilder =>
        {
            webBuilder.UseStartup<Startup>();
        });

}
复制代码
上面的代码:

配置使用 JSON 配置提供器使用下面的选项加载 MyConfig.json 文件:
optionsl: true: 文件是可选的
reloadOnChange: true: 当文件更改后保存会被重新加载
在读取 MyConfig.json 文件之前读取默认配置提供器(default configuration providers)。MyConfig.json 文件中的设置会覆盖默认配置提供器的配置,包含 Environment variables configuration provider 和 Command-line configuration provider。
一般的,你并不希望一个自定义的 JSON 文件去覆盖 Environment variables configuration provider 和 Command-line configuration provider 中的配置设置值。

下面的代码清除了所有的配置提供器并且添加了一些配置提供器:

复制代码
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}

public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
        .ConfigureAppConfiguration((hostingContext, config) =>
        {
            config.Sources.Clear();

            var env = hostingContext.HostingEnvironment;

            config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
                  .AddJsonFile($"appsettings.{env.EnvironmentName}.json", 
                                 optional: true, reloadOnChange: true);

            config.AddJsonFile("MyConfig.json", optional: true, reloadOnChange: true)
                  .AddJsonFile($"MyConfig.{env.EnvironmentName}.json",
                                 optional: true, reloadOnChange: true);

            config.AddEnvironmentVariables();

            if (args != null)
            {
                config.AddCommandLine(args);
            }
        })
        .ConfigureWebHostDefaults(webBuilder =>
        {
            webBuilder.UseStartup<Startup>();
        });

}
复制代码
在上面的代码中,MyConfig.json 和 MyConfig.Environment.json 文件中的设置:

覆盖了 appsetting.json 和 appsetting.Environment.json 文件中的设置
会被 Environment variables configuration provider 和 Command-line configuration provider 中的设置覆盖
示例程序(sample download)包含以下 MyConfig.json 文件:

复制代码
{
“Position”: {
“Title”: “My Config title”,
“Name”: “My Config Smith”
},
“MyKey”: “MyConfig.json Value”,
“Logging”: {
“LogLevel”: {
“Default”: “Information”,
“Microsoft”: “Warning”,
“Microsoft.Hosting.Lifetime”: “Information”
}
},
“AllowedHosts”: “*”
}
复制代码
下面的示例程序(sample download)中的代码展示了上面配置设置:

复制代码
public class TestModel : PageModel
{
// requires using Microsoft.Extensions.Configuration;
private readonly IConfiguration Configuration;

public TestModel(IConfiguration configuration)
{
    Configuration = configuration;
}

public ContentResult OnGet()
{
    var myKeyValue = Configuration["MyKey"];
    var title = Configuration["Position:Title"];
    var name = Configuration["Position:Name"];
    var defaultLogLevel = Configuration["Logging:LogLevel:Default"];


    return Content($"MyKey value: {myKeyValue} \n" +
                   $"Title: {title} \n" +
                   $"Name: {name} \n" +
                   $"Default Log Level: {defaultLogLevel}");
}

}
复制代码
XML 配置提供器
XmlConfigurationProvider 在运行时从 XML 文件中加载键值对配置。

下面的代码清空了所有的配置提供器并且添加了一些配置提供器:

复制代码
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}

public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
        .ConfigureAppConfiguration((hostingContext, config) =>
        {
            config.Sources.Clear();

            var env = hostingContext.HostingEnvironment;

            config.AddXmlFile("MyXMLFile.xml", optional: true, reloadOnChange: true)
                  .AddXmlFile($"MyXMLFile.{env.EnvironmentName}.xml",
                                 optional: true, reloadOnChange: true);

            config.AddEnvironmentVariables();

            if (args != null)
            {
                config.AddCommandLine(args);
            }
        })
        .ConfigureWebHostDefaults(webBuilder =>
        {
            webBuilder.UseStartup<Startup>();
        });

}
复制代码
在上面的代码中,MyXMLFile.xml 和 MyXMLFile.Environment.xml 文件中的设置会被以下配置中的设置覆盖:

Environment variables configuration provider
Command-line configuration provider.
示例程序(sample download)包含下面的 MyXMLFile.xml 文件:

复制代码

<?xml version="1.0" encoding="utf-8" ?> MyXMLFile Value Title from MyXMLFile Name from MyXMLFile Information Warning 复制代码 下面示例程序(sample download)中的代码展示了一些上面配置设置:

复制代码
public class TestModel : PageModel
{
// requires using Microsoft.Extensions.Configuration;
private readonly IConfiguration Configuration;

public TestModel(IConfiguration configuration)
{
    Configuration = configuration;
}

public ContentResult OnGet()
{
    var myKeyValue = Configuration["MyKey"];
    var title = Configuration["Position:Title"];
    var name = Configuration["Position:Name"];
    var defaultLogLevel = Configuration["Logging:LogLevel:Default"];


    return Content($"MyKey value: {myKeyValue} \n" +
                   $"Title: {title} \n" +
                   $"Name: {name} \n" +
                   $"Default Log Level: {defaultLogLevel}");
}

}
复制代码
那些使用相同元素名称的重复的元素,如果使用 name 属性用来区分的话,也是可以工作的:

复制代码

<?xml version="1.0" encoding="UTF-8"?>
value 00 value 01
value 10 value 11
复制代码 下面的配置读取前面的配置文件,展示了键和值:

复制代码
public class IndexModel : PageModel
{
private readonly IConfiguration Configuration;

public IndexModel(IConfiguration configuration)
{
    Configuration = configuration;
}

public ContentResult OnGet()
{
    var key00 = "section:section0:key:key0";
    var key01 = "section:section0:key:key1";
    var key10 = "section:section1:key:key0";
    var key11 = "section:section1:key:key1";

    var val00 = Configuration[key00];
    var val01 = Configuration[key01];
    var val10 = Configuration[key10];
    var val11 = Configuration[key11];

    return Content($"{key00} value: {val00} \n" +
                   $"{key01} value: {val01} \n" +
                   $"{key10} value: {val10} \n" +
                   $"{key10} value: {val11} \n"
                   );
}

}
复制代码
属性可以用来提供值:

复制代码

<?xml version="1.0" encoding="UTF-8"?>
复制代码 前面的配置文件使用下面的键和值加载:

key: attribute
section: key:attribute
Key-per-file 配置提供器
KeyPerFileConfigurationProvider 使用目录文件作为键值对配置。文件名称作为键。文件的内容作为值。Key-per-file 配置提供器在 Docker 托管的场景中使用。

为了启动 key-per-file 配置,可以调用 ConfigurationBuilder 实例的扩展方法 AddKeyPerFile。文件的 directoryPath 必须是绝对路径。

重载允许指定:

使用 Action 代理来配置源
目录和目录的路径是否可选
双下划线(__)用来在文件名中作为配置键的分隔符。例如,文件名 Loggin_LogLevel_System 会生成配置键 Logging:LogLevel:System。

在创建主机的时候调用 ConfigureAppConfiguration 来指定应用程序配置:

复制代码
.ConfigureAppConfiguration((hostingContext, config) =>
{
var path = Path.Combine(
Directory.GetCurrentDirectory(), “path/to/files”);
config.AddKeyPerFile(directoryPath: path, optional: true);
})
复制代码
内存配置提供器
MemoryConfigurationProvider 使用内存中的集合作为配置键值对。

下面的代码添加一个内存集合到配置系统:

复制代码
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}

public static IHostBuilder CreateHostBuilder(string[] args)
{
    var Dict = new Dictionary<string, string>
    {
       {"MyKey", "Dictionary MyKey Value"},
       {"Position:Title", "Dictionary_Title"},
       {"Position:Name", "Dictionary_Name" },
       {"Logging:LogLevel:Default", "Warning"}
    };

    return Host.CreateDefaultBuilder(args)
        .ConfigureAppConfiguration((hostingContext, config) =>
        {
            config.AddInMemoryCollection(Dict);
        })
        .ConfigureWebHostDefaults(webBuilder =>
        {
            webBuilder.UseStartup<Startup>();,
        });
}

}
复制代码
下面示例程序(sample download)中的代码展示了前面添加的配置中的设置:

复制代码
public class TestModel : PageModel
{
// requires using Microsoft.Extensions.Configuration;
private readonly IConfiguration Configuration;

public TestModel(IConfiguration configuration)
{
    Configuration = configuration;
}

public ContentResult OnGet()
{
    var myKeyValue = Configuration["MyKey"];
    var title = Configuration["Position:Title"];
    var name = Configuration["Position:Name"];
    var defaultLogLevel = Configuration["Logging:LogLevel:Default"];


    return Content($"MyKey value: {myKeyValue} \n" +
                   $"Title: {title} \n" +
                   $"Name: {name} \n" +
                   $"Default Log Level: {defaultLogLevel}");
}

}
复制代码
上面的代码中,config.AddInMemoryCollection(Dict) 在默认配置提供器(default configuration providers)之后添加。配置提供器的加载顺序的示例,请查看 JSON configuration provider。

查看 Bind an array 另一个使用 MemoryConfigurationProvider 的例子。

Kestrel endpoint 配置
Kestrel 指定的 endpoint 配置会覆盖所有 cross-server endpoint 的配置。Cross-server endpoint 配置包含:

UseUrls
命令行(command line)中的 --urls
环境变量(environment variable) ASPNETCORE_URLS
考虑下面在 ASP.NET Core web 应用程序中使用的 appsetting.json 文件:

复制代码
{
“Kestrel”: {
“Endpoints”: {
“Https”: {
“Url”: “https://localhost:9999”
}
}
},
“Logging”: {
“LogLevel”: {
“Default”: “Information”,
“Microsoft”: “Warning”,
“Microsoft.Hosting.Lifetime”: “Information”
}
},
“AllowedHosts”: “*”
}
复制代码
当前面高亮标记的配置在 ASP.NET Core web 应用程序中被使用,并且应用程序使用下面的 cross-server endpoint 配置在命令行中启动:

dotnet run --urls=“https://localhost:7777”

Kestrel 会绑定到 appsettings.json 文件中的 endpoint 特定的的配置(https://localhost:9999),而不是 https://localhost:7777。

考虑指定 Kestrel enpoint 配置作为一个环境变量:

set Kestrel__Endpoints__Https__Url=https://localhost:8888

在上面这个环境变量中,Https 是 Kestrel 指定的 endpoint 的名称。前面的 appsettings.json 文件也定义了指定的 endpoint 名称 Https. 默认的,使用 Environment Variables configuration provider 的环境变量在 appsettings.Environment.json 之后被读取,因此,上面的环境变量被用作 Https endpoint。

GetValue
ConfigurationBinder.GetValue 使用指定的键从配置文件中获取一个单独的值,并转换为指定类型:

复制代码
public class TestNumModel : PageModel
{
private readonly IConfiguration Configuration;

public TestNumModel(IConfiguration configuration)
{
    Configuration = configuration;
}

public ContentResult OnGet()
{
    var number = Configuration.GetValue<int>("NumberKey", 99);
    return Content($"{number}");
}

}
复制代码
在上面的代码中,如果 NumberKey 在配置中没有被发现,默认值 99 将会被使用。

GetSection, GetChildren,and Exists
下面的示例中,考虑以下 MySubsection.json 文件:

复制代码
{
“section0”: {
“key0”: “value00”,
“key1”: “value01”
},
“section1”: {
“key0”: “value10”,
“key1”: “value11”
},
“section2”: {
“subsection0”: {
“key0”: “value200”,
“key1”: “value201”
},
“subsection1”: {
“key0”: “value210”,
“key1”: “value211”
}
}
}
复制代码
下面的代码添加 MySubsection.json 到配置提供器中:

复制代码
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}

public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
        .ConfigureAppConfiguration((hostingContext, config) =>
        {
            config.AddJsonFile("MySubsection.json", 
                optional: true, 
                reloadOnChange: true);
        })
        .ConfigureWebHostDefaults(webBuilder =>
        {
            webBuilder.UseStartup<Startup>();
        });

}
复制代码
GetSection
IConfiguration.GetSection 使用一个子区域键返回一个配置子区域:

下面的代码返回 section1 的值:

复制代码
public class TestSectionModel : PageModel
{
private readonly IConfiguration Config;

public TestSectionModel(IConfiguration configuration)
{
    Config = configuration.GetSection("section1");
}

public ContentResult OnGet()
{
    return Content(
            $"section1:key0: '{Config["key0"]}'\n" +
            $"section1:key1: '{Config["key1"]}'");
}

}
复制代码
下面的代码返回 section2:subsection0 的值:

复制代码
public class TestSection2Model : PageModel
{
private readonly IConfiguration Config;

public TestSection2Model(IConfiguration configuration)
{
    Config = configuration.GetSection("section2:subsection0");
}

public ContentResult OnGet()
{
    return Content(
            $"section2:subsection0:key0 '{Config["key0"]}'\n" +
            $"section2:subsection0:key1:'{Config["key1"]}'");
}

}
复制代码
GetSection 从不返回 null。如果没有匹配的区域,一个空的 IConfigurationSection 被返回。

当 GetSection 返回一个匹配的区域,值(Value)不会被填充。当一个区域存在的时候,会返回一个键(Key)和路径(Path)。

GetChildren 和 Exists
下面的代码调用 IConfiguration.GetChildren,并返回 section2:subsection0 对应的值:

复制代码
public class TestSection4Model : PageModel
{
private readonly IConfiguration Config;

public TestSection4Model(IConfiguration configuration)
{
    Config = configuration;
}

public ContentResult OnGet()
{
    string s = null;
    var selection = Config.GetSection("section2");
    if (!selection.Exists())
    {
        throw new System.Exception("section2 does not exist.");
    }
    var children = selection.GetChildren();

    foreach (var subSection in children)
    {
        int i = 0;
        var key1 = subSection.Key + ":key" + i++.ToString();
        var key2 = subSection.Key + ":key" + i.ToString();
        s += key1 + " value: " + selection[key1] + "\n";
        s += key2 + " value: " + selection[key2] + "\n";
    }
    return Content(s);
}

}
复制代码
上面的代码调用 ConfigurationExtensions.Exists 验证指定区域是否存在。

绑定一个数组
ConfigurationBinder.Bind 支持使用配置中键中的数组索引绑定数组到一组对象。任何暴露一个数字类型键的段格式的数组,都是能够绑定到一个 POCO 类的数组。

考虑来自示例程序(sample download)中的 MyArray.json 文件:

复制代码
{
“array”: {
“entries”: {
“0”: “value00”,
“1”: “value10”,
“2”: “value20”,
“4”: “value40”,
“5”: “value50”
}
}
}
复制代码
下面的代码添加 MyArray.json 文件到配置提供器中:

复制代码
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}

public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
        .ConfigureAppConfiguration((hostingContext, config) =>
        {
            config.AddJsonFile("MyArray.json", 
                optional: true, 
                reloadOnChange: true);
        })
        .ConfigureWebHostDefaults(webBuilder =>
        {
            webBuilder.UseStartup<Startup>();
        });

}
复制代码
下面的代码读取配置并且显示出配置中的值:

复制代码
public class ArrayModel : PageModel
{
private readonly IConfiguration Config;
public ArrayExample _array { get; private set; }

public ArrayModel(IConfiguration config)
{
    Config = config;
}

public ContentResult OnGet()
{
    _array = Config.GetSection("array").Get<ArrayExample>();
    string s = null;

    for (int j = 0; j < _array.Entries.Length; j++)
    {
        s += $"Index: {j}  Value:  {_array.Entries[j]} \n";
    }

    return Content(s);
}

}
复制代码
上面的代码输出下面的内容:

Index: 0 Value: value00
Index: 1 Value: value10
Index: 2 Value: value20
Index: 3 Value: value40
Index: 4 Value: value50
上面的输出中,索引 3 对应的值是 value40,和文件 MyArray.json 中的 “4”:“value40” 相对应。绑定数组的索引是连续的,而不是绑定到配置键的值作为索引。配置绑定没有绑定空值或者为绑定对象创建空值的能力。

下面的代码使用 AddInMemoryCollection 扩展方法加载 array:entries 配置:

复制代码
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}

public static IHostBuilder CreateHostBuilder(string[] args)
{
    var arrayDict = new Dictionary<string, string>
    {
        {"array:entries:0", "value0"},
        {"array:entries:1", "value1"},
        {"array:entries:2", "value2"},
        //              3   Skipped
        {"array:entries:4", "value4"},
        {"array:entries:5", "value5"}
    };

    return Host.CreateDefaultBuilder(args)
        .ConfigureAppConfiguration((hostingContext, config) =>
        {
            config.AddInMemoryCollection(arrayDict);
        })
        .ConfigureWebHostDefaults(webBuilder =>
        {
            webBuilder.UseStartup<Startup>();
        });
}

}
复制代码
下面的代码读取 arrayDict Dictionary 中的配置并且显示输出对应的值:

复制代码
public class ArrayModel : PageModel
{
private readonly IConfiguration Config;
public ArrayExample _array { get; private set; }

public ArrayModel(IConfiguration config)
{
    Config = config;
}

public ContentResult OnGet()
{
    _array = Config.GetSection("array").Get<ArrayExample>();
    string s = null;

    for (int j = 0; j < _array.Entries.Length; j++)
    {
        s += $"Index: {j}  Value:  {_array.Entries[j]} \n";
    }

    return Content(s);
}

}
复制代码
上面的代码输出以下内容:

Index: 0 Value: value0
Index: 1 Value: value1
Index: 2 Value: value2
Index: 3 Value: value4
Index: 4 Value: value5
索引 #3 的绑定对象对应的配置数据是键 array:4 以及它对应的值。当包含一个数组的配置数据被绑定时,配置键对应的数组索引在创建对象时被用来迭代配置数据。配置数据不能引用一个空值,并且当一个数组在配置键跳过一个或者多个索引的时候,一个空的实体是不能被创建的。

缺失的索引为 #3 的配置项目可以在绑定到 ArrayExample 实例之前通过任意的配置提供器读取索引 $3 键值对来提供。

考虑示例程序中的 Value3.json 文件:

{
“array:entries:3”: “value3”
}
下面的代码包含 Value3.json 的配置和 arrayDict Dictionary:

复制代码
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}

public static IHostBuilder CreateHostBuilder(string[] args)
{
    var arrayDict = new Dictionary<string, string>
    {
        {"array:entries:0", "value0"},
        {"array:entries:1", "value1"},
        {"array:entries:2", "value2"},
        //              3   Skipped
        {"array:entries:4", "value4"},
        {"array:entries:5", "value5"}
    };

    return Host.CreateDefaultBuilder(args)
        .ConfigureAppConfiguration((hostingContext, config) =>
        {
            config.AddInMemoryCollection(arrayDict);
            config.AddJsonFile("Value3.json",
                                optional: false, reloadOnChange: false);
        })
        .ConfigureWebHostDefaults(webBuilder =>
        {
            webBuilder.UseStartup<Startup>();
        });
}

}
复制代码
下面的代码读取前面的配置并且显示配置值:

复制代码
public class ArrayModel : PageModel
{
private readonly IConfiguration Config;
public ArrayExample _array { get; private set; }

public ArrayModel(IConfiguration config)
{
    Config = config;
}

public ContentResult OnGet()
{
    _array = Config.GetSection("array").Get<ArrayExample>();
    string s = null;

    for (int j = 0; j < _array.Entries.Length; j++)
    {
        s += $"Index: {j}  Value:  {_array.Entries[j]} \n";
    }

    return Content(s);
}

}
复制代码
上面的代码输出以下内容:

复制代码
Index: 0 Value: value0
Index: 1 Value: value1
Index: 2 Value: value2
Index: 3 Value: value3
Index: 4 Value: value4
Index: 5 Value: value5
复制代码
自定义配置提供器不需要实现数组绑定。

自定义配置提供器
示例程序展示了如何创建一个基本的配置提供器,使用 Entity Framework (EF) 从数据库中读取键值对配置。

提供器有以下特性:

EF 内存数据库仅仅用于展示目的。如果要使用需要连接字符串的数据库,可以实现一个次要的 ConfigurationBuilder 支持来自其它配置提供器的连接字符串
提供器在启动时读取一个数据库的表到配置中。提供器获取数据库不基于基本的 per-key
Reload-on-change 没有实现,因此在应用程序启动后更新数据库对应用程序的配置没有影响
定义一个 EFConfigurationValue 实体用来存储数据库中的配置值。

Models/EFConfigurationValue.cs:

public class EFConfigurationValue
{
public string Id { get; set; }
public string Value { get; set; }
}
添加一个 EFConfigurationContext 用来存储和访问配置值。

EFConfigurationProvider/EFConfigurationContext.cs:

复制代码
// using Microsoft.EntityFrameworkCore;

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

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

}
复制代码
创建一个实现 IConfigurationSource 接口的类:

EFConfigurationProvider/EFConfigurationSource.cs:

复制代码
// using Microsoft.EntityFrameworkCore;
// using Microsoft.Extensions.Configuration;

public class EFConfigurationSource : IConfigurationSource
{
private readonly Action _optionsAction;

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

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

}
复制代码
通过继承 ConfigurationProvider 创建一个自定义的配置提供器。配置提供器初始化一个空的数据库。由于 configuration keys are case-insensitive 的原因,

使用 case-insensitive 的比较器(StringComparer.OrdinalIgnoreCase)创建的字典被用来初始化数据库。

EFConfigurationProvider/EFConfigurationProvider.cs:

复制代码
// using Microsoft.EntityFrameworkCore;
// using Microsoft.Extensions.Configuration;

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

Action<DbContextOptionsBuilder> OptionsAction { get; }

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>(StringComparer.OrdinalIgnoreCase)
        {
            { "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.

Extension/EntityFrameworkExtensions.cs:

复制代码
// using Microsoft.EntityFrameworkCore;
// using Microsoft.Extensions.Configuration;

public static class EntityFrameworkExtensions
{
public static IConfigurationBuilder AddEFConfiguration(
this IConfigurationBuilder builder,
Action optionsAction)
{
return builder.Add(new EFConfigurationSource(optionsAction));
}
}
复制代码
下面的代码展示了在 Program.cs 中如何使用 EFConfigurationProvider:

复制代码
// using Microsoft.EntityFrameworkCore;

public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureAppConfiguration((hostingContext, config) =>
{
config.AddEFConfiguration(
options => options.UseInMemoryDatabase(“InMemoryDb”));
})
复制代码
在 Startup 中获取配置
下面的代码在 Startup 的方法中显示了配置数据:

复制代码
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}

public IConfiguration Configuration { get; }

public void ConfigureServices(IServiceCollection services)
{
    services.AddRazorPages();
    Console.WriteLine($"MyKey : {Configuration["MyKey"]}");
}

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    Console.WriteLine($"Position:Title : {Configuration["Position:Title"]}");

    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseExceptionHandler("/Error");
        app.UseHsts();
    }

    app.UseHttpsRedirection();
    app.UseStaticFiles();

    app.UseRouting();

    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapRazorPages();
    });
}

}
复制代码
使用 startup 约定的方法获取配置的示例,请查看 App startup: Convenience methods。

在 Razor Pages 中获取配置
下面的代码在 Razor Page 中显示配置:

复制代码
@page
@model Test5Model
@using Microsoft.Extensions.Configuration
@inject IConfiguration Configuration

Configuration value for ‘MyKey’: @Configuration[“MyKey”]
复制代码
在下面的代码中,使用了 Configure 方法把 MyOptions 添加到服务容器中,并且绑定到配置:

复制代码
public void ConfigureServices(IServiceCollection services)
{
services.Configure(Configuration.GetSection(“MyOptions”));

services.AddRazorPages();

}
复制代码
下面的代码使用 @inject Razor directive 指令获取和显示选项的值:

复制代码
@page
@model SampleApp.Pages.Test3Model
@using Microsoft.Extensions.Options
@inject IOptions optionsAccessor

Option1: @optionsAccessor.Value.Option1

Option2: @optionsAccessor.Value.Option2

复制代码 在 MVC view 文件中获取配置 下面的代码在一个 MVC view 中展示配置数据:

@using Microsoft.Extensions.Configuration
@inject IConfiguration Configuration

Configuration value for ‘MyKey’: @Configuration[“MyKey”]
使用代理配置选项
代理中的选项配置会覆盖配置提供器中的值。

使用代理配置选项在示例程序中作为 Example 2 展示。

在下面的代码中,IConfigureOptions 服务被添加到服务容器中。它使用一个代理配置 MyOptions 的值:

复制代码
public void ConfigureServices(IServiceCollection services)
{
services.Configure(myOptions =>
{
myOptions.Option1 = “Value configured in delegate”;
myOptions.Option2 = 500;
});

services.AddRazorPages();

}
复制代码
下面的代码展示了选项的值:

复制代码
public class Test2Model : PageModel
{
private readonly IOptions _optionsDelegate;

public Test2Model(IOptions<MyOptions> optionsDelegate )
{
    _optionsDelegate = optionsDelegate;
}

public ContentResult OnGet()
{
    return Content($"Option1: {_optionsDelegate.Value.Option1} \n" +
                   $"Option2: {_optionsDelegate.Value.Option2}");
}

}
USB Microphone https://www.soft-voice.com/
Wooden Speakers https://www.zeshuiplatform.com/
亚马逊测评 www.yisuping.cn
深圳网站建设www.sz886.com

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值