4 配置系统
读取配置文件
var configBuilder = new ConfigurationBuilder();
configBuilder.AddJsonFile("config.json", optional: true, reloadOnChange: true);
var config = configBuilder.Build();
var name = config["name"];
Console.WriteLine(name);
var add = config.GetSection("proxy:add").Value;
Console.WriteLine(add);
var add2 = config.GetSection("proxy").Get<ProxyConfig>();
Console.WriteLine(add2.Add);
public class ProxyConfig
{
public string Add { get; set; }
}
选项方式读取配置
读取配置时, 有3种类型
- IOptions<T>
- 配置改变后, 不能读取到新的值, 必须重启程序才可以读到新的值
- 不监听配置的改变, 资源占用较少, 适用于对服务器启动后就不会改变的值进行读取
- IOptionsMonitor<T>
- 在配置改变后, 总能读到新的值
- IOptionsSnapshot<T>
- 在一个作用范围(如 Scope)内配置的值会保持一致(即使期间值更新了), 当离开作用范围后再次进入范围时, 才会读取到最新的值
- 使用 IOptionsSnapshot 更符合大部分场景的需求
使用命令行或环境变量时, 对于复杂结构的配置, 需要采用层级配置, 如: Proxy:Add=1000, 表示 Proxy 对象中的 Add 属性值为1000, 如果是数组, 就是这样: Num:0=1, Num:1=2
configBuilder.Addxxx() 可以指定多个, 如果存在同名, 优先使用最后的配置的值(后来的覆盖前边的配置值)
var configBuilder = new ConfigurationBuilder();
// 通过json文件
// configBuilder.AddJsonFile("config.json", optional: true, reloadOnChange: true);
// 通过命令行
//configBuilder.AddCommandLine(args);
// 通过环境变量(一般加个前缀, 避免和其它环境变量混在一块)
configBuilder.AddEnvironmentVariables();
var configRoot = configBuilder.Build();
ServiceCollection services = new ServiceCollection();
services.AddOptions().Configure<ProxyConfig>((e) => { configRoot.Bind("proxy", e); });
services.AddScoped<TestConfigConroller>();
using (var sp = services.BuildServiceProvider())
{
while (true)
{
// 创建Scope来产生一个范围, IOptionsSnapshot在这个范围内保持数值一致, 离开作用范围之后, 重新获取数值时会取得最新的值
using (var sc = sp.CreateScope())
{
var tc = sc.ServiceProvider.GetRequiredService<TestConfigConroller>();
tc.Test();
Console.WriteLine("更改配置文件, 可以发现下边读出来的值还是一样的");
Console.ReadKey();
var tc2 = sc.ServiceProvider.GetRequiredService<TestConfigConroller>();
tc2.Test();
}
Console.WriteLine("出Scope的范围了, 按下以继续");
Console.ReadKey();
}
}
public class TestConfigConroller
{
private readonly IOptionsSnapshot<ProxyConfig> _proxyConfig;
public TestConfigConroller(IOptionsSnapshot<ProxyConfig> proxyConfig)
{
_proxyConfig = proxyConfig;
}
public void Test()
{
Console.WriteLine(_proxyConfig.Value.Add);
Console.WriteLine("----------");
Console.WriteLine(_proxyConfig.Value.Add);
}
}
config.json
{
"name": "abc",
"proxy": {
"add": "192.168"
}
}
自定义配置提供者(读取XML)
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<connectionStrings>
<add name="conn" connectionString="Data Source=.;Initial Catalog=DB;User ID=sa;Password=1234" providerName="Hsin.SqlClient"/>
<add name="conn2" connectionString="Data Source=127.0.0.1;Initial Catalog=DB;User ID=sa;Password=12345" providerName="Hsin.MyClient"/>
</connectionStrings>
<appsettings>
<add key="config:name" value="smtp.test.com"/>
<add key="config:proxy:add" value="abc123"/>
<add key="config:proxy:ids:0" value="12"/>
<add key="config:proxy:ids:1" value="45"/>
</appsettings>
</configuration>
var configBuilder = new ConfigurationBuilder();
configBuilder.AddFxConfig("web.config");
var configRoot = configBuilder.Build();
var services = new ServiceCollection();
services.AddOptions().Configure<WebConfig>(e => configRoot.Bind(e));
services.AddScoped<TestWebConfig>();
using (var serviceProvider = services.BuildServiceProvider())
{
var tw = serviceProvider.GetRequiredService<TestWebConfig>();
tw.Test();
}
public class WebConfig
{
public ConnectionStr Conn { get; set; }
public ConnectionStr Conn2 { get; set; }
public Config Config { get; set; }
}
public class Config
{
public string Name { get; set; }
public ProxyConfig Proxy { get; set; }
}
public class ProxyConfig
{
public string Add { get; set; }
public List<int> Ids { get; set; }
}
public class ConnectionStr
{
public string ConnectionString { get; set; }
public string ProviderName { get; set; }
}
public class FxConfigProvider : FileConfigurationProvider
{
public FxConfigProvider(FileConfigurationSource source) : base(source)
{
}
public override void Load(Stream stream)
{
var data = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
var xd = new XmlDocument();
xd.Load(stream);
var csNodes = xd.SelectNodes("/configuration/connectionStrings/add");
foreach (var node in csNodes.Cast<XmlNode>())
{
var name = node.Attributes["name"].Value;
var connectionString = node.Attributes["connectionString"].Value;
// 多层级的属性
data[$"{name}:connectionString"] = connectionString;
var attProviderName = node.Attributes["providerName"];
if (attProviderName != null)
{
data[$"{name}:providerName"] = attProviderName.Value;
}
}
var asNodes = xd.SelectNodes("/configuration/appsettings/add");
foreach (var node in asNodes.Cast<XmlNode>())
{
var key = node.Attributes["key"].Value;
key = key.Replace(".", ":");
var value = node.Attributes["value"].Value;
data[key] = value;
}
Data = data;
}
}
public class FxConfigSource : FileConfigurationSource
{
public override IConfigurationProvider Build(IConfigurationBuilder builder)
{
// 加这一句能够正确获取到目录下的文件(不然会提示找不到文件)
EnsureDefaults(builder);
return new FxConfigProvider(this);
}
}
public static class FxConfigExtensions
{
// 写Addxxx来简化过程
public static IConfigurationBuilder AddFxConfig(this IConfigurationBuilder builder, string path = null)
{
if (path == null)
{
path = "web.config";
}
builder.Add(new FxConfigSource { Path = path });
return builder;
}
}
public class TestWebConfig
{
private readonly IOptionsSnapshot<WebConfig> _webConfig;
public TestWebConfig(IOptionsSnapshot<WebConfig> webConfig)
{
_webConfig = webConfig;
}
public void Test()
{
var wc = _webConfig.Value;
Console.WriteLine($"Conn:ConnectionString: {wc.Conn.ConnectionString}, ProviderName: {wc.Conn.ProviderName}");
Console.WriteLine($"Config:Name: {wc.Config.Name}, Config:Proxy:Add: {wc.Config.Proxy.Add}");
}
}
No Comments