配置系统官方文档:https://learn.microsoft.com/zh-cn/dotnet/core/extensions/configuration
.Net配置系统支持丰富的配置源包括文件、注册表、环境变量、命令行、Azure Key Vault等,还可以配置自定义配置源。可以跟踪配置的改变,可以按照优先级覆盖。
还支持ini、xml等格式的配置源。
还支持在运行时、调试时加载不同的json文件。
还内置或第三方支持中心化配置服务器,比如使用Apollo、Nacos等开源服务器,或使用Azure、阿里云等的配置服务

JSON配置

安装Microsoft.Extensions.Configuration(配置框架的包)和Microsoft.Extensions.Configuration.Json(读json的包)
新建json文件,设置属性如果较新就赋值。
config.json:

1
2
3
4
5
{
"name": "张三",
"age": "18",
"proxy": {"address": "aa","port": "8080"}
}

原始使用方式

AddJsonFile()命名参数介绍
optional表示文件是否可选
reloadOnChange参数表示如果文件修改了,是否重新加载设置(可以不重启就加载)

1
2
3
4
5
6
7
8
9
10
11
static void Main(string[] args) {
ConfigurationBuilder configurationBuilder = new ConfigurationBuilder();
configurationBuilder.AddJsonFile("config.json", optional: true, reloadOnChange: true);
IConfigurationRoot configurationRoot = configurationBuilder.Build();
//原始方式
string name = configurationRoot["name"];
string address = configurationRoot.GetSection("proxy:address").Value;
Console.WriteLine($"name = {name}");
Console.WriteLine($"address = {address}");
Console.ReadLine();
}

绑定对象方式

安装Microsoft.Extensions.Configuration.Binder
有自动类型转换,自定义的类的属性名应与配置文件中的键名一致(拼写一致就行,不区分大小写)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
internal class Program {
static void Main(string[] args) {
ConfigurationBuilder configurationBuilder = new ConfigurationBuilder();
configurationBuilder.AddJsonFile("config.json", optional: true, reloadOnChange: true);
IConfigurationRoot configurationRoot = configurationBuilder.Build();
//绑定对象方式
Proxy proxy = configurationRoot.GetSection("proxy").Get<Proxy>();
Console.WriteLine($"{proxy.Address}:{proxy.Port}");
Config config = configurationRoot.Get<Config>();
Console.WriteLine($"{config.Name},{config.Age},{config.Proxy.Address},{config.Proxy.Port}");
}
}
class Config {
public string Name { get; set; }
public string Age { get; set; }
public Proxy Proxy { get; set; }
}
class Proxy {
public string Address { get; set; }
public int Port { get; set; }
}

依赖注入方式(更推荐)

在需要读取配置的地方,用IOptionsSnapshot<T>注入。不要在构造函数里直接读取IOptionsSnapshot.Value,而是到用到的地方再读取,否则就无法更新变化。
读取配置时,DI要声明IOptions<T>(旧的值)、IOptionsMonitor<T>(新的值)、IOptionsSnapshot<T>(一个范围内新的值)等类型,建议用IOptionsSnapshot。
安装:
Microsoft.Extensions.Configuration.Binder
Microsoft.Extensions.Configuration
Microsoft.Extensions.Configuration.Json

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
internal class Program {
static void Main(string[] args) {
ServiceCollection services = new ServiceCollection();
services.AddScoped<TestController>();
services.AddScoped<Test2>();

ConfigurationBuilder configurationBuilder = new ConfigurationBuilder();
configurationBuilder.AddJsonFile("config.json", optional: true, reloadOnChange: true);
IConfigurationRoot configurationRoot = configurationBuilder.Build();

services.AddOptions()
.Configure<Config>(e => configurationRoot.Bind(e))//DI将根节点绑定到Config对象上并且将Config类型注册到依赖注入容器中
.Configure<Proxy>(e=> configurationRoot.GetSection("proxy").Bind(e));//将proxy节点绑定到Proxy对象上并且将Proxy类型注册到依赖注入容器中

using(var sp = services.BuildServiceProvider()) {
//因为我们使用了IOptionSnapshot,所以修改文件时不会改变,相当于在一个大scope中
while (true) {
//如果想改变时获取新的数据,可以手动新建scope
using(var scope = sp.CreateScope()) {
var c = scope.ServiceProvider.GetRequiredService<TestController>();
c.Test();
Console.WriteLine("改一下age");
Console.ReadLine();
c.Test();
var c2 = scope.ServiceProvider.GetRequiredService<Test2>();
c2.Test();
//注意:你无法通过以下方式获取配置对象
//var config = scope.ServiceProvider.GetRequiredService<Config>();
}
Console.WriteLine("点击任意键继续");
Console.ReadLine();
}
}

}
}
class Config {
public string Name { get; set; }
public string Age { get; set; }
public Proxy Proxy { get; set; }
}
class Proxy {
public string Address { get; set; }
public int Port { get; set; }
}
internal class TestController {
//创建IOptionsSnapshot<>类型成员,为依赖注入提供条件
public readonly IOptionsSnapshot<Config> optConfig;
public TestController(IOptionsSnapshot<Config> optConfig) {
this.optConfig = optConfig;
}
public void Test() {
Console.WriteLine(this.optConfig.Value.Age);
Console.WriteLine("===============");
}
}
internal class Test2 {
private readonly IOptionsSnapshot<Proxy> optProxy;
public Test2(IOptionsSnapshot<Proxy> optProxy) {
this.optProxy = optProxy;
}
public void Test() {
Console.WriteLine(optProxy.Value.Address);
}
}

命令行配置

命令行简单配置:

安装Microsoft.Extensions.Configuration.CommandLine
支持 server = 127.0.0.1、–server=127.0.0.1、 –server 127.0.0.1等,注意在键值之间加空格,格式不能混用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
internal class Program {
static void Main(string[] args) {
ServiceCollection services = new ServiceCollection();
services.AddScoped<TestController>();
services.AddScoped<Test2>();
ConfigurationBuilder configBuder = new ConfigurationBuilder();
configBuder.AddCommandLine(args);//推荐传这个
IConfigurationRoot configRoot = configBuder.Build();
services.AddOptions()
.Configure<Config>(e => configRoot.Bind(e))
.Configure<Proxy>(e => configRoot.GetSection("Proxy"));
using(var sp = services.BuildServiceProvider()) {
while(true) {
using(var scope = sp.CreateScope()) {
var c1 = scope.ServiceProvider.GetRequiredService<TestController>();
var c2 = scope.ServiceProvider.GetRequiredService<Test2>();
c1.Test();
c2.Test();
Console.ReadLine();
}
}
}
}
}
public class Config {
public string Name { get; set; }
public int Age { get; set; }
public Proxy proxy { get; set; }
}
public class Proxy {
public string Address { get; set; }
public int Port { get; set; }
}
internal class TestController {
public readonly IOptionsSnapshot<Config> optConfig;
public TestController(IOptionsSnapshot<Config> optConfig) {
this.optConfig = optConfig;
}
public void Test() {
Console.WriteLine(this.optConfig.Value.Age);
Console.WriteLine("===============");
}
}
internal class Test2 {
private readonly IOptionsSnapshot<Proxy> optProxy;
public Test2(IOptionsSnapshot<Proxy> optProxy) {
this.optProxy = optProxy;
}
public void Test() {
Console.WriteLine(optProxy.Value.Address);
}
}

可以这样运行:

或者使用vs右键项目->属性->调试->启动配置文件:

扁平化处理

对于多级结构需要用root:key=value的方式来进行扁平化处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class Config {
public string Name { get; set; }
public int Age { get; set; }
public Proxy proxy { get; set; }
}
public class Proxy {
public string Address { get; set; }
public int Port { get; set; }
public int[] Ids { get; set; }
}
internal class TestController {
public readonly IOptionsSnapshot<Config> optConfig;
public TestController(IOptionsSnapshot<Config> optConfig) {
this.optConfig = optConfig;
}
public void Test() {
Console.WriteLine(optConfig.Value.Name);
Console.WriteLine(optConfig.Value.Age);
Console.WriteLine("===============");
Console.WriteLine(optConfig.Value.proxy.Address);
Console.WriteLine(optConfig.Value.proxy.Port);
Console.WriteLine(string.Join(',', optConfig.Value.proxy.Ids));
}
}

环境变量


注意遇到深层次结构还是需要做扁平化处理

1
configBuder.AddEnvironmentVariables();

还可以传一个名为prefix的字符串参数,这个参数是所需要的环境变量的前缀为了防止和系统其他环境变量冲突,记得修改自己写的环境变量为其添加前缀。

开发自己的配置提供者

1、开发一个直接或间接实现IConfigurationProvider接口的类XXXConfigurationProvider,一般直接继承ConfigurationProvider。如果是从文件读取,可以继承自FileConfigurationProvider。重写Load方法,把”扁平化数据”设置到Data属性即可。
2、再开发一个实现了IConfigurationSource接口的类XXXIConfigurationSource。如果是从文件读取,可以继承自FileConfigurationSource。在Build方法中返回上面的ConfigurationProvider对象。
3、然后使用即可,configurationBuilder.Add(new ConfigurationSource())即可。为了简化使用,一般提供一个IConfigurationBuilder的扩展方法
整体流程:
编写ConfigurationProvider类实际读取配置;编写ConfigurationSource在Build中返回ConfigurationProvider对象;把ConfigurationSource对象加入IConfigurationBuilder。记得在返回之前加一句EnsureDefaults(builder)用于处理默认值问题。
配置文件格式:
开发web.config提供者
.net core中不建议使用.net framework里的web.config,不过仍然继续提供了ConfigurationManager的方式。我们来实现一个能够读取web.config里的connectionStrings和appSettings的内容
具体内容我没看明白,请看杨中科老师的视频吧:https://www.bilibili.com/video/BV1pK41137He?p=43&vd_source=b855e022b9c5c6c637f3aa9babc2e438

多配置源

比如某个网站需要自定义配置;程序员的同一台机器上,开发调试环境和测试环境用不同的配置。
按照注册到ConfigurationBuilder的顺序,后来者居上,后注册的优先级高,如果配置名字重复,用后注册的值

保命的UserSecrets

.net提供了user-secrets机制,user-secrets的配置不会放到源代码中
不能泄露到源码中的配置放到user-secrets即可,不用都放
一般把user-secrets优先级放到普通json文件之后。
如果开发人员电脑重装系统等原因造成本地的配置文件被删除了,就需要重新配置
并不是生产中的加密,只适用于开发
安装:
Microsoft.Extensions.Configuration.UserSecrets
在vs项目上点右键【管理用户机密】,编辑这个配置文件。看看这个文件在哪里。在csproj中的UserSecretsld就是文件夹的名字
configBuilder.AddUserSecrets<Program>()

1
2
3
4
//secrets.json
{
"password":"secret"
}
1
2
3
4
5
//主程序
var builder = new ConfigurationBuilder();
builder.AddJsonFile("config.json", true, true);
builder.AddCommandLine(args);
builder.AddUserSecrets<Program>();

记得在除了secrets.json以外的所有配置地方删除私密的数据。

总结

依赖注入

1
2
3
4
5
6
7
8
//创建容器
ServiceCollection services = new ServiceCollection();
//添加服务
services.AddScoped<TestController>();
//创建提供方
var sp = services.BuildServiceProvider();
//获取对象
sp.GetService<Test>();

配置系统

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//创建配置创建者 
ConfigurationBuilder configurationBuilder = new ConfigurationBuilder();
//添加配置方
configurationBuilder.AddJsonFile("config.json", optional: true, reloadOnChange: true);
//创建根节点
IConfigurationRoot configurationRoot = configurationBuilder.Build();
//通过根节点找到数据
string address = configurationRoot.GetSection("proxy:address").Value;
//或绑定对象
Proxy proxy = configurationRoot.GetSection("proxy").Get<Proxy>();
//或通过依赖注入绑定对象
services.AddOptions()
.Configure<Config>(e => configurationRoot.Bind(e))
.Configure<Proxy>(e=> configurationRoot.GetSection("proxy").Bind(e));