8.1 基础
1.传统Web.config配置的缺点,无法完成配置集群的管理
2.为了兼容传统的.NET Framework,仍然可以使用Web.config和ConfigurationManager类,但不推荐了
3..NET中的配置系统支持丰富的配置源,包括文件(json\xml\ini等)、注册表、环境变量、命令行、Azure Key Vault等,还可以配置自定义配置源。可以跟踪配置的改变,可以按照优先级覆盖。
8.1.1 Json文件配置
注意下面代码中optional false表示json文件存在就读取,不存在就不读 true表示不存在就报错
reloadOnChange:配置文件更改后是否重新加载。
Microsoft.Extensions.Configuration的使用
JSON文件:
{
"AppConfig": {
"DbConnection": "Server=;port=;database=",
"EnableTrace": false,
"IpWhiteList": [
"127.0.0.1",
"1.1.2.1",
"2.3.1.5"
],
"Port": 123,
"ServiceName": "myapi"
},
"Student": [
{
"name": "yzk",
"age": 18
},
{
"name": "albert",
"age": 18
}
],
"Teacher": [
{
"name": "albertzhao",
"age": 25
},
{
"name": "yangzhongke",
"age": 40
}
]
}
代码:
using Microsoft.Extensions.Configuration;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
namespace _210722_Demon01_JsonConfigure {
class Program {
static void Main(string[] args) {
ConfigurationBuilder configurationBuilder = new ConfigurationBuilder();
configurationBuilder.AddJsonFile("webconfig.json", true, true);
IConfigurationRoot configurationRoot = configurationBuilder.Build();
AppConfig appConfig = new AppConfig();
//取子范围的一个元素值
appConfig.DbConnection = configurationRoot["AppConfig:DbConnection"];
//取子范围元素值的第二种写法,先获得一个域,域下面的["DbConnection"]
appConfig.EnableTrace = configurationRoot.GetSection("AppConfig")["EnableTrace"];
//取数组的写法 取AppConfig:IpWhiteList的全部子元素,选取子元素中的value
appConfig.IpWhiteList = (IEnumerable<string>)configurationRoot.GetSection("AppConfig:IpWhiteList").GetChildren().Select(x=>x.Value);
foreach (var item in appConfig.IpWhiteList) {
Console.WriteLine(item);
}
Console.ReadLine();
}
}
class AppConfig {
public string DbConnection { get; set; }
public string EnableTrace { get; set; }
public IEnumerable<string> IpWhiteList { get; set; }
}
}
8.1.2 Json序列化
https://www.json.cn/json/json2csharp.html Json转换为实体类
8.1.3 Json绑定读取配置(*)
用https://www.json.cn/json/json2csharp.html进行json转C#类,直接new实例,根节点的大类直接root.Get
using System;
using System.Collections.Generic;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Configuration.Json;
namespace _210723_Demon01_JsonTransferClass {
class Program {
static void Main(string[] args) {
ConfigurationBuilder configurationBuilder = new ConfigurationBuilder();
configurationBuilder.AddJsonFile("webconfig.json", false, true);
var configurationRoot = configurationBuilder.Build();
//绑定一个对象来进行转换
Root root = configurationRoot.Get<Root>();
AppConfig appConfig = configurationRoot.GetSection("AppConfig").Get<AppConfig>();
Console.WriteLine(appConfig.DbConnection);
Console.ReadLine();
}
}
public class AppConfig {
/// <summary>
///
/// </summary>
public string DbConnection { get; set; }
/// <summary>
///
/// </summary>
public string EnableTrace { get; set; }
/// <summary>
///
/// </summary>
public List<string> IpWhiteList { get; set; }
/// <summary>
///
/// </summary>
public int Port { get; set; }
/// <summary>
///
/// </summary>
public string ServiceName { get; set; }
}
public class StudentItem {
/// <summary>
///
/// </summary>
public string name { get; set; }
/// <summary>
///
/// </summary>
public int age { get; set; }
}
public class TeacherItem {
/// <summary>
///
/// </summary>
public string name { get; set; }
/// <summary>
///
/// </summary>
public int age { get; set; }
}
public class Root {
/// <summary>
///
/// </summary>
public AppConfig AppConfig { get; set; }
/// <summary>
///
/// </summary>
public List<StudentItem> Student { get; set; }
/// <summary>
///
/// </summary>
public List<TeacherItem> Teacher { get; set; }
}
}
8.2 IOptionsSnapshot 选项配置
目录结构:
webconfig内容:
{
"AppConfig": {
"DbConnection": "Server=;port=;database=",
"EnableTrace": false,
"IpWhiteList": [
"127.0.0.1",
"1.1.2.1",
"2.3.1.5"
],
"Port": 55155,
"ServiceName": "albertservice"
},
"Student": [
{
"name": "yzk",
"age": 18
},
{
"name": "albert",
"age": 18
}
],
"Teacher": [
{
"name": "albertzhao",
"age": 25
},
{
"name": "yangzhongke",
"age": 40
}
]
}
Root类:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace _210723_Demon02_JsonDIIOptionsSnapshot {
public class Root {
/// <summary>
///
/// </summary>
public AppConfig AppConfig { get; set; }
/// <summary>
///
/// </summary>
public List<StudentItem> Student { get; set; }
/// <summary>
///
/// </summary>
public List<TeacherItem> Teacher { get; set; }
}
public class AppConfig {
/// <summary>
///
/// </summary>
public string DbConnection { get; set; }
/// <summary>
///
/// </summary>
public string EnableTrace { get; set; }
/// <summary>
///
/// </summary>
public List<string> IpWhiteList { get; set; }
/// <summary>
///
/// </summary>
public int Port { get; set; }
/// <summary>
///
/// </summary>
public string ServiceName { get; set; }
}
public class StudentItem {
/// <summary>
///
/// </summary>
public string name { get; set; }
/// <summary>
///
/// </summary>
public int age { get; set; }
}
public class TeacherItem {
/// <summary>
///
/// </summary>
public string name { get; set; }
/// <summary>
///
/// </summary>
public int age { get; set; }
}
}
IController接口和实现类以及扩展类TestControllerExtensions
interface IController {
void ReadAppConfig();
}
using Microsoft.Extensions.Options;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace _210723_Demon02_JsonDIIOptionsSnapshot {
class TestController : IController {
public readonly IOptionsSnapshot<Root> optionsSnapshot;
public TestController(IOptionsSnapshot<Root> optionsSnapshot) {
this.optionsSnapshot = optionsSnapshot;
}
public void ReadAppConfig() {
//此处注意IOptionsSanpshot.Value才是实例
Console.WriteLine(optionsSnapshot.Value.AppConfig.Port);
Console.WriteLine(optionsSnapshot.Value.AppConfig.ServiceName);
}
}
}
public static class TestControllerExtensions {
public static void AddTestController(this IServiceCollection service) {
service.AddScoped<IController, TestController>();
}
}
AppConfig类以及AppConfigExtensions类:
public class AppConfigController {
public readonly IOptionsSnapshot<AppConfig> optionsSnapshot;
public AppConfigController(IOptionsSnapshot<AppConfig> optionsSnapshot) {
this.optionsSnapshot = optionsSnapshot;
}
public void ShowAppConfigPort() {
Console.WriteLine(this.optionsSnapshot.Value.Port);
}
}
public static class AppConfigControllerExtensions {
public static void AddAppConfig(this IServiceCollection services) {
services.AddScoped<AppConfigController>();
}
}
主程序:
new ServiceCollection—services.AddTestController()—new ConfigurationBuilder—configuretionBuilder.AddJson(xxx)—var rootConfig = configuretionBuilder.Build()—
services.AddOptions().Configure
此处必须将reloadOnChange设置为true,每次改变都要加载
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using System;
namespace _210723_Demon02_JsonDIIOptionsSnapshot {
class Program {
static void Main(string[] args) {
var services = new ServiceCollection();
//依赖注入,注入TestController对象和AppConfigController对象
services.AddTestController();
services.AddAppConfig();
//services.AddScoped<IController, TestController>();
//services.AddScoped<AppConfigController>();
ConfigurationBuilder configurationBuilder = new ConfigurationBuilder();
//此处必须将reloadOnChange设置为true,每次改变都要加载
configurationBuilder.AddJsonFile("webconfig.json", false, true);
var rootConfig = configurationBuilder.Build();
//这边将rootConfig根节点绑定到Root对象上
//将rootConfig的子域对象绑定到AppConfig对象
services.AddOptions()
.Configure<Root>(e => rootConfig.Bind(e))
.Configure<AppConfig>(e=>rootConfig.GetSection("AppConfig").Bind(e));
using (var sp = services.BuildServiceProvider()) {
//上面必须进行根节点对象绑定到Root上,不然无法将IOptionsSnapshot<Root>对象通过构造函数注入
while (true) {
//如果此处不进行sp.CreateScope(),即使修改了本地的webconfig.json的内容也无效
using (var scope = sp.CreateScope()) {
scope.ServiceProvider.GetRequiredService<IController>().ReadAppConfig();
scope.ServiceProvider.GetService<AppConfigController>().ShowAppConfigPort();
}
Console.WriteLine("请按任意键继续");
Console.ReadKey();
}
}
//Console.ReadLine();
}
}
}
8.3 其他配置提供者
8.3.1 命令行方式配置
·配置框架还支持命令行参数、环境变量等地方读取
·NuGet安装 Microsoft.Extension.Configuration.CommandLine
·configBuilder.AddCommandLine(args)
·参数支持多种格式,比如:server=127.0.0.1、—server=127.0.0.1(注意在键值之间加空格)、/server=127.0.0.1、/server 127.0.0.1(注意在键值之间加空格)。格式不能混用。
·调试的时候,VS中简化命令行传参数的方法。
代码在Gitee中necore项目210907——Demo01_OterConfigProvider项目
扁平化配置
如果是多级结构,通过:来分开 proxy:address=szdxzhy@outlook.com(这样来赋值)
如果是数组,则需要:0 :1 :2来配置
8.3.2 环境变量方式配置
下面图片写错了,包的名字为Microsoft.Extensions.Configuration.EnvironmentVariables
如果使用带参数的,如configBuilder.AddEnvironmentVariables(“Albert_”),则需要在环境变量中添加前缀,防止修改全局的环境变量。
8.3.4 其他配置源
8.3.5 多配置源
8.3.6 User-secrets机制
user-secrets的配置不放到源代码中,Nuget安装Microsoft.Extensions.Configuration.UserSecrets。
在VS项目上右键【管理用户机密】,编辑配置文件,configurationBuilder.AddUserSecrets
下图中usrsecrets放在普通配置文件之前:
//依赖注入的容器Collection
var serviceCollection = new ServiceCollection();
//注入Config和Log
serviceCollection.AddConfigService();
serviceCollection.AddLogService();
serviceCollection.AddMailService();
//设置Option
ConfigurationBuilder configurationBuilder = new ConfigurationBuilder();
//[多配置源问题--后面的配置覆盖前面的配置]
//通过数据库来获取中心配置,先写死进行测试
//SqlConnection需要安装Nuget:System.Data.SqlClient
//AddDbConfiguration来自Zack.AnyDBConfigProvider
string strConfigFromSqlserver = "Server=192.168.0.238;Database=AlbertXDataBase;Trusted_Connection=True;";
configurationBuilder.AddDbConfiguration(() => new SqlConnection(strConfigFromSqlserver),
reloadOnChange: true,
reloadInterval: TimeSpan.FromSeconds(2),
tableName: "T_Configs");
//从不泄密文件中读取账号密码:Secrets.Json
configurationBuilder.AddUserSecrets<Program>();
//如果要启用本地Json文件读取,则启用下面的代码,通过AddJsonFile来加载相关扁平化配置信息。
configurationBuilder.AddJsonFile("AlbertConfig/mailconfig.json", false, true);
//控制台
configurationBuilder.AddCommandLine(args);
//环境变量
configurationBuilder.AddEnvironmentVariables("Albert_");
//从mailconfig读取回来的根节点
var rootConfig = configurationBuilder.Build();
//绑定根节点到MailKitRoot实体对象上
//这边的GetSection里面的字段是Json字符串的字段,一定要注意这名字和实体类的属性名
//一定要相同,不然无法绑定成功
//ToDo:NetCore下的AddOption原理剖析。
serviceCollection.AddOptions().Configure<MailKitRoot>(e => rootConfig.Bind(e))
.Configure<MailBodyEntity>(e => rootConfig.GetSection("MailBodyEntity").Bind(e))
.Configure<SendServerConfigurationEntity>(e => rootConfig.GetSection("SendServerConfigurationEntity").Bind(e));
//使用DI,Build一个服务提供者
using (var sp = serviceCollection.BuildServiceProvider())
{
var sendMailResult = sp.GetRequiredService<IMailService>().SendMail();
Console.WriteLine(sendMailResult.ResultInformation);
Console.WriteLine(sendMailResult.ResultStatus);
}
}
8.4 配置优先级
读取配置默认顺序,后者会覆盖前者:
现有的IConfiguration(一般不需要在这个前面加)
项目根目录下appsettings.json
项目根目录下appsettings.{Environment}.json
用户机密
环境变量
命令行参数
读取appsetting.json中的keyvalue,通常数据库连接字符串放在这边
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*",
"MyKey": "我是嘻嘻嘻"
}
Startup.cs类想获取,想要注入IConfiguration
public Startup(IConfiguration configuration) {
Configuration = configuration;
}
public IConfiguration Configuration { get; }
获取代码
乱码问题,需要将appsetting.json文件保存为UTF-8-无标签
VS设置“高级保存选项”:https://www.cnblogs.com/willingtolove/p/12121577.html
.NET3.1
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) {
if (env.IsDevelopment()) {
app.UseDeveloperExceptionPage();
}
else {
app.UseExceptionHandler("/Error");
}
app.Run(
async context =>
{
//此处设置没啥用
context.Response.ContentType = "text/plain;charset=utf-8";
await context.Response.WriteAsync(Configuration["MyKey"]);
}
);
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints => {
endpoints.MapRazorPages();
});
}
.NET6-Program.cs
var builder = WebApplication.CreateBuilder(args);
//内存缓存
builder.Services.AddMemoryCache();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
//运行环境读取方法
if (app.Environment.EnvironmentName == "AlbertTest")
{
Console.WriteLine(app.Environment.EnvironmentName);
}
通过命令行来配置参数,到项目目录下,cmd
dotnet run MyKey=”我是命令行参数”
8.5 开发SelfProvider
Reinvent the wheel 重新造轮子=> 价值是是什么?对于程序员而言,一定要有自己造轮子的能力。
从学习这到创造者!
8.5.1 开发web.config提供者
- FxConfigurationProvider
- 为项目添加Nuget包,Microsoft.Extensions.Configuration和Microsoft.Extensions.Configuration.FileExtensions
- 创建类FxConfigProvider,继承自FileConfigurationProvider抽象类
- 重写Load方法,其中Stream是文件流,从FileConfigurationSource中获取返回信息。
- 在Load方法中将字符串拍平,通常的格式是“{第一级名称}:第二级名称”作为键,第二级名称作为值,对应起来,如下图:
- FxConfigurationSource
同时在FxConfigurationProvider复写方法Load中,最后需要添加this.Data = data,将生成的字典赋值给它。
8.5.2 开发数据库配置提供者
中心化配置服务器
- 安装Sql Server:前往官网安装:https://www.microsoft.com/en-us/sql-server/sql-server-downloads
安装Developer或者基于云的,本机安装SSMS(https://docs.microsoft.com/zh-cn/sql/ssms/download-sql-server-management-studio-ssms?view=sql-server-ver15)进行数据库管理。验证数据库是否可以进行IP登录,如果不能按照如下配置并尝试关闭防火墙:数据库无法以IP登录问题解决。
- 配置数据库,登录进去后,建库建表,表三个字段:ID(自增主键),Name,Value。
ID设置:设置主键—是标识(双击即可)
- 将配置信息写入数据库中,Value支持Json格式
- 将Json映射成C#类
安装Nuget程序包:https://www.nuget.org/packages/Zack.AnyDBConfigProvider/
Install-Package Zack.AnyDBConfigProvider -Version 1.1.3
Install-Package System.Data.SqlClient -Version 4.8.2
代码中调用:
string strConfigFromSqlserver = "Server=192.168.0.238;Database=AlbertXDataBase;Trusted_Connection=True;";
configurationBuilder.AddDbConfiguration(() => new SqlConnection(strConfigFromSqlserver),
reloadOnChange: true,
reloadInterval: TimeSpan.FromSeconds(2));
8.6 配置系统综合案例
功能需求:
系统的主要配置放到配置专用的数据库中
- 连接配置数据库的连接字符串配置在用户机密中
- 把Smtp的配置显示到界面上
- 程序启动就连接Redis,并把Redis连接对象著恶策到依赖注入的系统中
操作:如何在Program.cs里面读取配置
在appsettings.json或者其他机密文件中添加数据库连接字符串
"SqlServer": {
"ConnectStr": "Server = .; Database = AlbertConfigDb; Trusted_Connection = True;MultipleActiveResultSets=true;Connect Timeout=500"
}
安装Zack.AnyDBConfigProvider包-安装System.Data.SqlClient
在Program中添加如下代码:
//Zack.AnyDBConfigProvider里面的GetConnectionString读取的字符串是配置文件中的
//而不是数据库中的,连接上数据库后,AddDbConfiguration才是从数据库中读取Json
//这里Host和WebHost一个是主机一个是通用主机
//如果想要启用builder.Configuration.GetConnectionString("con");
//则需要在appsettings.json中配置“ConnectionStrings:con":"xxx"
builder.WebHost.ConfigureAppConfiguration((hostCtx, configBuilder) => {
var env = hostCtx.HostingEnvironment;
var connStr = builder.Configuration.GetValue<string>("SqlServer:ConnectStr");
configBuilder.AddDbConfiguration(() =>
new SqlConnection(connStr),
reloadOnChange: true,
reloadInterval: TimeSpan.FromSeconds(2),
tableName: "ProduceToolConfig");
//开发模式下,保存项目程序集到用户机密
if (env.IsDevelopment() && !string.IsNullOrEmpty(env.ApplicationName))
{
var appAssembly = Assembly.Load(new AssemblyName(env.ApplicationName));
if (appAssembly != null)
{
configBuilder.AddUserSecrets(appAssembly, optional: true);
}
}
});
安装StackExchange.Redis包,这样就可以直接启动Redis了
//AddDbConfiguration之后即可从数据库中拿取Json对了
builder.Services.AddSingleton<IConnectionMultiplexer>(sp =>
{
//在Program.cs中读取配置的一种方法
var connStr = builder.Configuration.GetValue<string>("RedisServer:Configuration");
return ConnectionMultiplexer.Connect(connStr);
});
读取数据库中配置信息,直接绑定到SmtpSettings类上
public class SmtpSettings
{
public string ServerName { get; set; }
public string UserName { get; set; }
public string Password { get; set; }
}
//配置选项:Smtp 注意这里的根是appsettings.json
builder.Services.AddOptions().Configure<SmtpSettings>(smtp => builder.Configuration.GetSection("SmtpSettings").Bind(smtp));
//调用
using AlbertZhao.cn.Models;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using StackExchange.Redis;
namespace AlbertZhao.cn.Controllers
{
[Route("api/[controller]/[action]")]
[ApiController]
public class SmtpController : ControllerBase
{
private readonly ILogger<SmtpController> logger;//日志服务
private readonly IOptionsSnapshot<SmtpSettings> options;//配置选项服务
private readonly IConnectionMultiplexer connectionMultiplexer;//Redis服务
public SmtpController(ILogger<SmtpController> logger, IOptionsSnapshot<SmtpSettings> options, IConnectionMultiplexer connectionMultiplexer)
{
this.logger = logger;
this.options = options;
this.connectionMultiplexer = connectionMultiplexer;
}
[HttpGet]
public ActionResult<SmtpSettings?> GetSmtpInfo()
{
logger.LogInformation("开始获取数据");
return new SmtpSettings() { ServerName = options.Value.ServerName ,
UserName = options.Value.UserName ,
Password = options.Value.Password
};
}
[HttpGet]
public ActionResult<string?> GetRedisInfo()
{
logger.LogInformation("开始测试Redis");
var ping = connectionMultiplexer.GetDatabase(0).Ping();
return ping.ToString();
}
}
}