ASP.NET Core 中的配置
前言
配置在我们开发过程中必不可少,asp.net中的配置在 web.config
中。也可配置在如:json、xml、数据库等(但asp.net并没提供相应的模块和方法)。
在asp.net core中web.config已经不存在了(但如果托管到 iis 的时候可以使用 web.config 配置 iis),
而是用appsettings.json和appsettings.(development、staging、production).json配置文件
(可以理解为asp.net中的web.config和web.release.config的关系)。
下面我们一起看下asp.net core 中的配置
基础用法
homecontroller.cs
:
[apicontroller] public class homecontroller : controllerbase { private readonly iconfiguration _configuration; public homecontroller(iconfiguration configuration) { _configuration = configuration; } [httpget("/")] public dynamic index() { return jsonconvert.serializeobject(new { connectionstring = _configuration["connectionstring"], child1 = _configuration["parent:child1"], grandchildren = _configuration["parent:child2:grandchildren"] }); } }
返回结果:
{ "connectionstring": "data source=.;initial catalog=test;user id=sa", "child1": "child", "grandchildren": "grandchildren" }
对应appsettings.json
的值:
{ "connectionstring": "data source=.;initial catalog=test;user id=sa;password=123", "parent": { "child1": "child1", "child2": "child2" } }
需要注意的:
- 键不区分大小写。 例如,connectionstring 和 connectionstring 被视为等效键。
-
如果键名相同(包含所有加载的配置文件),以最后一个值为准。
所以
appsettings.(development、staging、production).json
会覆盖appsettings.json
的配置信息。- 多层级用
:
来分割,但如果是环境变量则存在跨平台问题 - 上面这种写法是没缓存的即:
appsettings.json
修改后,获取的值会立即改变
- 多层级用
绑定到对象 ioptions<testoptions>
对于_configuration["parent:child2:grandchildren"]
的写法对程序员来说肯定很反感,下面我们看下把配置文件绑定到对象中。
-
首先把 json 对象转换成对象
public class testoptions { public string connectionstring { get; set; } public parent parent { get; set; } } public class parent { public string child1 { get; set; } public child2 child2 { get; set; } } public class child2 { public string grandchildren { get; set; } }
-
在
startup.cs
的configureservices
方法添加代码:services.configure<testoptions>(configuration);
-
homecontroller.cs
:[apicontroller] public class homecontroller : controllerbase { private readonly ioptions<testoptions> _options; public homecontroller(ioptions<testoptions> options) { _options = options; } [httpget("options")] public string options() { return jsonconvert.serializeobject(_options); } }
返回的结果如下:
{ "value": { "connectionstring": "data source=.;initial catalog=test;user id=sa", "parent": { "child1": "child", "child2": { "grandchildren": "grandchildren" } } } }
发现
- 如果我们修改
appsettings.json
,然后刷新页面,会发现值并没用变 -
我们发现根节点是
value
根据上面 2 个问题我们去看源代码:
首先找到这句:
services.tryadd(servicedescriptor.singleton(typeof(ioptions<>), typeof(optionsmanager<>)));
我们去看
optionsmanager
方法:public class optionsmanager<toptions> : ioptions<toptions>, ioptionssnapshot<toptions> where toptions : class, new() { private readonly ioptionsfactory<toptions> _factory; // 注解: _cache 为 concurrentdictionary<string, lazy<toptions>>() // 不知道注意到上面注入方法没,用的是 singleton 单例,所以更新配置文件后没及时更新 private readonly optionscache<toptions> _cache = new optionscache<toptions>(); public optionsmanager(ioptionsfactory<toptions> factory) { _factory = factory; } /// <summary> /// toptions的默认配置实例 /// 这里就是为什么根节点为value /// </summary> public toptions value { get { return get(options.defaultname); } } /// <summary> /// 该方法在 ioptionssnapshot 接口中 /// </summary> public virtual toptions get(string name) { name = name ?? options.defaultname; // store the options in our instance cache return _cache.getoradd(name, () => _factory.create(name)); } }
绑定到对象 ioptionssnapshot<testoptions>
-
与
ioptions<testoptions>
的用法一样,区别就是依赖注入的生命周期为scoped,所以没缓存,直接看代码:services.tryadd(servicedescriptor.scoped(typeof(ioptionssnapshot<>), typeof(optionsmanager<>)));
-
ioptionssnapshot<toptions> 比 ioptions<toptions> 多了个方法
toptions get(string name);
value
属性其实就是调用了该方法,只是name
为options.defaultname
ioptionssnapshot<toptions> 继承了 ioptions<toptions> (这里有个疑问,上面的
optionsmanager<toptions>
继承了ioptions<toptions>, ioptionssnapshot<toptions>
2 个接口,也许是为了清晰吧)。
自定义配置文件(json)
有的时候配置信息很多,想在单独的文件中配置,asp.net core 提供了ini、xml、json文件系统加载配置的方法。
-
首先在 program.cs的
createwebhostbuilder
方法中添加如下代码:public static iwebhostbuilder createwebhostbuilder(string[] args) { return webhost.createdefaultbuilder(args) .configureappconfiguration((hostingcontext, config) => { //设置根目录,我们放在 /config 文件夹下 // 此种写法不会加载 `appsettings.json` // config.setbasepath(path.combine(directory.getcurrentdirectory(), "config")); config.setbasepath(directory.getcurrentdirectory()); //添加 setting.json 配置文件 //optional: 文件是否可选,当为false时 如果文件不存在 程序启动不起来 //reloadonchange:文件改变后是否重新加载 config.addjsonfile("config/setting.json", optional: false, reloadonchange: false); }) .usestartup<startup>(); }
我们看下 reloadonchange 参数处理了哪些事情:
if (source.reloadonchange && source.fileprovider != null) { changetoken.onchange( //监视文件 如果改变就执行 load(true) 方法 () => source.fileprovider.watch(source.path), () => { thread.sleep(source.reloaddelay); load(reload: true); }); }
-
创建setting.json:
{ "rookie": { "name": "ddd", "age": 12, "sex": "男" } }
-
在 controller 中:
[route("api/[controller]")] [apicontroller] public class optionscontroller : controllerbase { private readonly iconfiguration _configuration; public optionscontroller(iconfiguration configuration) { _configuration = configuration; } [httpget] public string index() { return jsonconvert.serializeobject(new { name = _configuration["rookie:name"], age = _configuration["rookie:age"], sex = _configuration["rookie:sex"] }); } }
自定义配置文件(json)绑定到对象
通过上面配置发现获取配置用的是 _configuration["name"]
方式,下面我们绑定到对象上
定义配置对象 setting 类
-
在startup.cs的
configureservices
方法添加代码:services.configure<setting>(configuration.getsection("rookie"));
getsection
方法:获得指定键的配置子部分 用法和上面一样
不知道发现个情况没,我们setting.json的配置文件的信息也是存到_configuration中的,如果文件多了很可能会覆盖节点,下面我们换一种写法。
-
首先删除 program.cs中
createwebhostbuilder
方法的代码:config.addjsonfile("config/setting.json", optional: false, reloadonchange: true); -
修改配置文件setting.json:
{ "name": "ddd", "age": 12, "sex": "男" }
-
在中startup.cs的
configureservices
方法中添加:var config = new configurationbuilder() .setbasepath(directory.getcurrentdirectory()) .addjsonfile("config/setting.json", optional: false, reloadonchange: true) .build(); services.configure<setting>(config);
这样就不会影响 全局 的配置信息了
其他方式
-
我们看下下面 2 个方式:
[route("[controller]")] [apicontroller] public class homecontroller : controllerbase { private readonly iconfiguration _configuration; private readonly testoptions _bindoptions = new testoptions(); private readonly parent _parent = new parent(); public homecontroller(iconfiguration configuration) { //全部绑定 _configuration.bind(_bindoptions); //部分绑定 _configuration.getsection("parent").bind(_parent); } [httpget("options")] public string options() { return jsonconvert.serializeobject(new { bindoptions = _bindoptions, parent = _parent }); } }
个人感觉这种方式有点多余 ( ╯□╰ )
-
ioptionsmonitor、ioptionsfactory、ioptionsmonitorcache 方式:
我们看如下代码
services.tryadd(servicedescriptor.singleton(typeof(ioptions<>), typeof(optionsmanager<>))); services.tryadd(servicedescriptor.scoped(typeof(ioptionssnapshot<>), typeof(optionsmanager<>))); services.tryadd(servicedescriptor.singleton(typeof(ioptionsmonitor<>), typeof(optionsmonitor<>))); services.tryadd(servicedescriptor.transient(typeof(ioptionsfactory<>), typeof(optionsfactory<>))); services.tryadd(servicedescriptor.singleton(typeof(ioptionsmonitorcache<>), typeof(optionscache<>)));
这里只说下
ioptionsmonitor<>
:[route("[controller]")] [apicontroller] public class homecontroller : controllerbase { private readonly ioptionsmonitor<testoptions> _optionsmonitor; public homecontroller(ioptionsmonitor<testoptions> optionsmonitor , ilogger<homecontroller> logger) { _optionsmonitor = optionsmonitor; _logger = logger; } [httpget("options")] public string options() { //这里有个变更委托 _optionsmonitor.onchange((options, name) => { var info = jsonconvert.serializeobject(new { options = options, name = name }); _logger.loginformation($"配置信息已更改:{info}"); }); return jsonconvert.serializeobject(new { optionsmoitor = _optionsmonitor }); }
当我们修改配置文件后会看到日志信息:
info: webapisample.controllers.homecontroller[0]
配置信息已更改:{"options":{"connectionstring":"data source=.;initial catalog=test;user id=sa","parent":{"child1":"child","child2":{"grandchildren":"grandchildren"}}},"name":"testoptions"}
info: webapisample.controllers.homecontroller[0]
配置信息已更改:{"options":{"connectionstring":"data source=.;initial catalog=test;user id=sa","parent":{"child1":"child","child2":{"grandchildren":"grandchildren"}}},"name":"testoptions"}
info: webapisample.controllers.homecontroller[0]
配置信息已更改:{"options":{"connectionstring":"data source=.;initial catalog=test;user id=sa","parent":{"child1":"child","child2":{"grandchildren":"grandchildren"}}},"name":"testoptions"}
info: webapisample.controllers.homecontroller[0]
配置信息已更改:{"options":{"connectionstring":"data source=.;initial catalog=test;user id=sa","parent":{"child1":"child","child2":{"grandchildren":"grandchildren"}}},"name":"testoptions"}不知道为什么会有这么多日志...
还有一点不管我们修改哪个日志文件,只要是执行了
addjsonfile
的文件,都会触发该事件
写在最后
写的有点乱希望不要见怪,本以为一个配置模块应该不会复杂,但看了源代码后发现里面的东西好多... 本来还想写下是如何实现的,但感觉太长了就算了。 最后还是希望大家以批判的角度来看,有任何问题可在留言区留言!
上一篇: 前端基础之BOM和DOM
推荐阅读
-
(24)ASP.NET Core EF查询(查询的工作原理、跟踪与非跟踪查询)
-
在Asp.Net或.Net Core中配置使用MarkDown富文本编辑器有开源模板代码(代码是.net core3.0版本)
-
ASP.NET Core 3.0 : 二十八. 在Docker中的部署以及docker-compose的使用
-
我来告诉你:VS2019开发ASP.NET Core 3.0 Web项目,修改视图后,刷新浏览器看不到修改后的效果怎么处理
-
.NET Core Razor Pages中ajax get和post的使用
-
汇总:ASP.NET Core中HttpContext获取传参数据,有哪些方式
-
在 ASP.NET Core 中启用跨域请求(CORS)
-
在Asp.Net Core中配置使用MarkDown富文本编辑器实现图片上传和截图上传(开源代码.net core3.0)
-
记一次EF Core DBContext在Action委托中GC异常的问题.
-
ASP中DLL的调试环境配置全攻略