.NET 配置框架
.NET 框架本身集成了强大的配置功能,支持多种配置源数据读写。
经常使用的 nuget 包:
1
2
3
4
5
6
Microsoft.Extensions.Configuration.Abstractions //抽象包
Microsoft.Extensions.Configuration //实现包
Microsoft.Extensions.Configuration.Binder //强类型绑定
Microsoft.Extensions.Configuration.Json //配置Json文件
Microsoft.Extensions.Primitives //配置验证
Microsoft.Extensions.Options //配置选项
框架核心接口:
1
2
3
4
IConfigurationBuilder //建造者接口
IConfigurationRoot //配置根
IConfigurationSource //配置源接口
IConfigurationProvider //配置提供程序接口
配置框架应用了建造者模式,我们需要将配置源提交给建造者,通过建造者接口提供的 build
方法,创建IConfigurationRoot
实例,它是配置的根,我们可以通过 IConfigurationRoot
实例访问到配置文件。通过实现 IConfigurationSource
, IConfigurationProvider
两个接口,我们也可以在框架中添加自定义的配置源。
WebApi 项目代码示例
在 appsettings.json
中设置配置文件
1
2
3
4
5
6
7
{
"key1": "nihao",
"section1": {
"key1": [ "1", "2" ],
"key2":4
}
}
构造函数中添加 IConfiguration
接口,并使用 _configuration
读取配置
1
2
3
4
5
6
7
8
9
10
11
12
13
private readonly IConfiguration _configuration;
public WeatherForecastController(IConfiguration configuration)
{
_configuration = configuration;
}
[HttpGet]
public string Get()
{
Console.WriteLine(_configuration["key1"]); //读取配置
Console.WriteLine(_configuration.GetSection("section1")["key2"]);
return "OK";
}
因为 ASP.NET Core
框架已经默认添加了 appsettings.json
的配置源,所以我们直接使用 IConfiguration
接口就可以读取到其中的配置,如果我们需要添加新的配置文件则需要手动添加(注意:后添加的配置源,会覆盖掉与前面配置源相同 key
的 value
值。)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureAppConfiguration(builder => {
builder.AddJsonFile("dbsettings.json", false, true); //添加新的配置源 optional:是否可选,为false时,启动时未找到该配置文件会程序异常。 reloadOnChange:配置变更时,是否重新加载配置。
})
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
控制台程序实例
1
2
3
4
IConfigurationBuilder configurationBuilder = new ConfigurationBuilder();
configurationBuilder.AddJsonFile("appsettings.json", false, true);
var iconfigurationRoot = configurationBuilder.Build();
Console.WriteLine($"key1 {iconfigurationRoot["key1"]}");
强类型绑定
配置框架支持将配置文件与类型绑定,它可以让我们使用配置更加方便,也使配置结构更加清晰。
创建一个 SectionOptions
类型
1
2
3
4
5
public class SectionOptions
{
public string[] key1 { get; set; }
public int key2 { get; set; } = 2;
}
在 ConfigureServices
方法中添加如下代码
1
2
3
4
5
6
7
8
var config = new SectionOptions()
{
};
Configuration.GetSection("section1").Bind(config, options => //将配置文件中的section1分段与config实例绑定
{
options.BindNonPublicProperties = true; //支持私有属性绑定
});
services.AddSingleton<SectionOptions>(config);
这样我们就可以在构造函数中引入SectionOptions
,并通过它来读写配置文件了。
配置变更监听
给配置文件变更事件设置回调函数,当配置文件更新时,我们可以在回调函数中,做相应的逻辑处理
1
2
3
4
5
6
7
ChangeToken.OnChange(() =>
{
return Configuration.GetReloadToken();
}, () =>
{
Console.WriteLine(Configuration["key1"]);
});
配置选项
配置选项模式让类型绑定变得更加简单、灵活。它支持三种模式:IOptions
,IOptionsMonitor
,IOptionsSnapshot
。
在 ConfigureServices
中添加如下代码
1
services.Configure<SectionOptions>(Configuration.GetSection("section1"));
在构造函数中通过 IOptions
模式使用 SectionOptions
,它不能动态重载配置的变更
1
2
3
4
5
private readonly SectionOptions _sectionOptions;
public WeatherForecastController(IOptions<SectionOptions> options)
{
_sectionOptions = options.Value;
}
在构造函数中通过 IOptionsMonitor
模式使用 SectionOptions
,它支持动态重载配置的变更,并保证每次读取的配置值都是最新的
1
2
3
4
5
private readonly SectionOptions _sectionOptions;
public WeatherForecastController(IOptionsMonitor<SectionOptions> options)
{
_sectionOptions = options.CurrentValue;
}
在构造函数中通过 IOptionsSnapshot
模式使用 SectionOptions
,它支持动态重载配置的变更,但同一次请求域内,多次请求的值保持一致(可用来防止单次请求内由于配置文件变更引起的数据处理问题)
1
2
3
4
5
private readonly SectionOptions _sectionOptions;
public WeatherForecastController(IOptionsSnapshot<SectionOptions> options)
{
_sectionOptions = options.Value;
}
配置验证
配置框架还提供了三种配置验证的机制,当配置错误时,使用该配置程序会抛出异常。
DataAnnotations
特性验证,在模型中使用 DataAnnotations
特性标记
1
2
3
services.AddOptions<SectionOptions>().Configure(option => {
Configuration.GetSection("section1").Bind(option);
}).ValidateDataAnnotations();
函数验证,在绑定配置时,直接注入验证函数
1
2
3
4
5
6
services.AddOptions<SectionOptions>().Configure(option => {
Configuration.GetSection("section1").Bind(option);
}).Validate(section =>
{
return section.key2 > 1;
});
实现 IValidateOptions
接口
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class SectionValidateOptions : IValidateOptions<SectionOptions>
{
public ValidateOptionsResult Validate(string name, SectionOptions options)
{
if (options.key2 > 100)
{
return ValidateOptionsResult.Fail("key2 不能大于100");
}
else
{
return ValidateOptionsResult.Success;
}
}
}
//在ConfigureServices方法中添加如下代码
services.AddOptions<SectionOptions>().Configure(option => {Configuration.GetSection("section1").Bind(option);}).Services.AddSingleton<IValidateOptions<SectionOptions>, SectionValidateOptions>();
后配置处理
配置框架还提供了方法让我们对配置数据进行处理
1
2
3
4
services.PostConfigure<SectionOptions>(options =>
{
options.key2 += 10;
});
自定义配置源
通过实现下面的两个接口,我们可以实现自己的配置提供程序,它可以用来获取远程配置中心的配置。
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
public class MyConfigurationSource : IConfigurationSource
{
public IConfigurationProvider Build(IConfigurationBuilder builder)
{
return new MyConfigurationProvider();
}
}
public class MyConfigurationProvider : ConfigurationProvider, IConfigurationProvider
{
public MyConfigurationProvider()
{
var timer = new Timer();
timer.Elapsed += Timer_Elapsed;
timer.Interval = 3000;
timer.Start();
}
private void Timer_Elapsed(object sender, ElapsedEventArgs e)
{
Load(true);
}
public void Load()
{
Load(false);
}
private void Load(bool reload = false)
{
//可以在这里加载远程配置中心的配置
this.Data["lastdatetime"] = $"{DateTime.Now}";
if (reload)
{
base.OnReload();
}
}
}
添加自定义配置源
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureAppConfiguration(builder => {
// builder.Add(new MyConfigurationSource()); //添加自定义配置源
})
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
这样我们就可以通过配置框架获取到自定义配置源中的数据了。