.NET Core 2.x中使用Named Options处理多个强类型配置实例
来源: Using multiple instances of strongly-typed settings with named options in .NET Core 2.x
作者: Andrew Lock
译者: Lamond Lu
.NET Core从1.0版本开始,就已经开始使用Options
模式绑定强类型配置对象。从那时起到现在,这个特性已经获得了更多的功能。例如在.NET Core 1.1中引入的IOptionsSnapshot
类。使用这个类的好处是,当你的配置文件(例如: appsetting.json)发生变化时,它可以帮助我们自动刷新我们的强类型配置对象。
本篇博客中,我们将讨论在依赖注入容器中注册强类型配置的多个实例的几种方式。我将特别说明如何使用Named Options
方式来完成注入。
使用强类型配置
Options
模式将POCO对象和IConfiguration
对象绑定,从而实现强类型配置。因为这一过程我已经在之前一篇博文中介绍过,所以这里我就简述一下。
我们可以将强类型配置对象和配置绑定起来,并注入到你的服务中。
public class SlackApiSettings
{
public string WebhookUrl { get; set; }
public string DisplayName { get; set; }
}
你可以在Startup
类的ConfigureServices
中使用Configure
将强类型配置对象和配置中的一个节点绑定起来。
public void ConfigureServices(IServiceCollection services)
{
services.Configure<SlackApiSettings>(Configuration.GetSection("SlackApi"));
}
以上代码中,Configure
方法将你的配置和SlackApiSettings
对象绑定了起来。除了以上方式,Configure
方法还提供了一个参数为Action
的重载,所以你来可以使用如下的方式绑定配置。
public void ConfigureServices(IServiceCollection services)
{
services.Configure<SlackApiSettings>(x => x.DisplayName = "My Slack Bot");
}
你可以通过在服务中注入IOptions
对象来访问配置好的SlackApiSettings
对象。
public class SlackNotificationService
{
private readonly SlackApiSettings _settings;
public SlackNotificationService(IOptions<SlackApiSettings> options)
{
_settings = options.Value
}
public void SendNotification(string message)
{
// use the settings to send a message
}
}
你可以使用IOptions.Value
属性,获取到配置好的强类型对象。
除了以上方式,你还可以注入一个IOptionsSnapshot
接口对象。
使用IOptionsSnapshot
处理配置变化
到目前为止,我所展示的例子都是最典型的用法。但是当我们使用IOption
来读取强类型配置时,这意味着你的配置在程序生命周期中是不变的。即配置对象只会计算和绑定一次。假如你在程序运行过程中,更改了appSettings.json文件,程序读取配置时,依然会得到程序启动时的配置对象,而非你修改过之后的配置对象。
对我个人而言,对于大部分场景,使用IOption
已经能够解决所有问题。但是如果程序确实需要支持重新加载配置,我们还可以使用ASP.NET Core中的IOptionsSnapshot
。IOptionsSnapshot
和IOptions
使用方法一样,因此你无需在应用程序中执行任何额外的操作。你只需要使用IOptionsSnapshot.Value
属性读取配置对象即可。
public class SlackNotificationService
{
private readonly SlackApiSettings _settings;
public SlackNotificationService(IOptionsSnapshot<SlackApiSettings> options)
{
_settings = options.Value;
}
}
使用以上方式,如果你在程序启动后,修改了appSettings.json文件,IOptionsSnapshot
会在下一次请求时,更新配置值,你就能获取到新的配置值了。这里需要注意的是配置值的生命周期是Scoped
, 即在一次请求中,读取到的配置值都是一样的。
注意: 并不是所有的配置提供器都支持配置重新加载。文件类型的配置器都没有问题,但是环境变量配置器就不可以。
重新加载配置在某些情况下可能很有用,但IOptionsSnapshot
还有另一个技巧 - 命名选项(Named Options)。 我们很快就会介绍它们,但首先我们将看一下你可能偶尔遇到的问题,您需要拥有多个设置对象实例。
使用一个强类型配置对象的多个实例
IOption
的典型用例就是针对细粒度的配置。配置系统让你很容易的为特定服务注入小的,集中的POCO对象。但是如果你需要配置多个具有相同属性的配置对象时,应该怎么做的?例如,为了将消息发送到Slack
, 你需要一个Webhook Url和一个DisplayName. 当你调用SendNotification(message)
时,SlackNotificationService
会使用这些配置来向指定的Slack Channel中发送消息。
如果你想更新SlackNotificationService以允许你向多个频道发送消息,该怎么办?
public class SlackNotificationService
{
public void SendNotificationToDevChannel(string message) { }
public void SendNotificationToGeneralChannel(string message) { }
public void SendNotificationToPublicChannel(string message) { }
}
这里我已经为创建了向不同的频道发送消息的方法。但是问题是,我该如何为每个频道配置其对应的Webhook Url
和DisplayName
。
为了提供一些思路,这里我们假设我们的配置文件结构是这样的。
{
"SlackApi": {
"DevChannel" : {
"WebhookUrl": "https://hooks.slack.com/T1/B1/111111",
"DisplayName": "c0mp4ny 5l4ck b07"
},
"GeneralChannel" : {
"WebhookUrl": "https://hooks.slack.com/T2/B2/222222",
"DisplayName": "Company Slack Bot"
},
"PublicChannel" : {
"WebhookUrl": "https://hooks.slack.com/T3/B3/333333",
"DisplayName": "Professional Looking name"
}
}
为了在SlackNotificationService
中读取到响应的配置,这里有3种可行的方案。
1. 创建父类配置对象
第一种方式就是扩展SlackApiSettings
类,在其中包含各个频道的配置属性。
public class SlackApiSettings
{
public ChannelSettings DevChannel { get; set; }
public ChannelSettings GeneralChannel { get; set; }
public ChannelSettings PublicChannel { get; set; }
public class ChannelSettings
{
public string WebhookUrl { get; set; }
public string DisplayName { get; set; }
}
}
这里我创建了一个内嵌类ChannelSettings
, 并在SlackApiSettings
类中添加了针对3个Slack Channel的配置。这个新的配置类,正确反映了appSettings.json文件中的配置结构。
public void ConfigureServices(IServiceCollection services)
{
services.Configure<SlackApiSettings>(Configuration.GetSection("SlackApi"));
}
在SlackNotificationService
中,我们还是和之前一样注入的单个配置类对象
public class SlackNotificationService
{
private readonly SlackApiSettings _settings;
public SlackNotificationService(IOptions<SlackApiSettings> options)
{
_settings = options.Value;
}
}
这种配置方式的优点是易于理解,我们为每个Slack Channel配置了独立的强类型配置。缺点是如果要支持新的Slack Channel, 你每次都需要修改SlackApiSettings
类。
2. 为每个Channel配置创建单独的配置类
另外一种方法是我们可以独立配置每个Slack Channel。我们分开配置不同的Slack Channel, 并把他们注入到SlackNotificationService
服务中。
例如,我们将ChannelSettings
类变成一个抽象类
public abstract class ChannelSettings
{
public string WebhookUrl { get; set; }
public string DisplayName { get; set; }
}
然后每一个Slack Channel的配置类继承ChannelSettings
类。
public class DevChannelSettings: ChannelSettings { }
public class GeneralChannelSettings: ChannelSettings { }
public class PublicChannelSettings: ChannelSettings { }
为了配置不同的Slack Channel, 我们需要在程序启动时分别绑定不同的配置节点。
public void ConfigureServices(IServiceCollection services)
{
services.Configure<DevChannelSettings>(Configuration.GetSection("SlackApi:DevChannel"));
services.Configure<GeneralChannelSettings>(Configuration.GetSection("SlackApi:GeneralChannel"));
services.Configure<PublicChannelSettings>(Configuration.GetSection("SlackApi:PublicChannel"));
}
由于不同的Slack Channel拥有不同的配置,所以我们需要分开将他们注入到SlackNotificationService
中。
public class SlackNotificationService
{
private readonly DevChannelSettings _devSettings;
private readonly GeneralChannelSettings _generalSettings;
private readonly PublicChannelSettings _publicSettings;
public SlackNotificationService(
IOptions<DevChannelSettings> devOptions
IOptions<GeneralChannelSettings> generalOptions
IOptions<PublicChannelSettings> publicOptions)
{
_devSettings = devOptions;
_generalSettings = generalOptions;
_publicSettings = publicOptions;
}
}
这种方式的好处是当你需要添加新的Slack Channel配置时,你不需要去修改之前定义的配置类结构,你只需要添加一个针对新Slack Channel的配置类。但是它也让事情更加复杂了,你不仅需要为每个新的Slack Channel配置类绑定配置的节点, 还需要修改SlackNotificationService
的构造函数添加对新Slack Channel配置类的依赖。
3. 使用Named Options
第三种方案就是本文的主题Named Options
。Named Options
翻译过来就是命名配置,和它的字面意思一样,我们可以使用它为每个强类型配置对象起一个唯一的名称,并在使用时通过指定唯一名称来获取所需的强类型配置对象。
使用Named Options
, 你可以拥有同一个强类型配置类的不同实例,并独立配置他们。这意味着,我们可以继续使用本文开头所定义的SlackApiSettings
类。
public class SlackApiSettings
{
public string WebhookUrl { get; set; }
public string DisplayName { get; set; }
}
区别是,当我们配置强类型配置对象的代码有所不同。
public void ConfigureServices(IServiceCollection services)
{
services.Configure<SlackApiSettings>("Dev", Configuration.GetSection("SlackApi:DevChannel"));
services.Configure<SlackApiSettings>("General", Configuration.GetSection("SlackApi:GeneralChannel"));
services.Configure<SlackApiSettings>("Public", Configuration.GetSection("SlackApi:PublicChannel"));
}
我们使用的Configure
的2个参数的重载方法,其中第一个参数指定了一个唯一名称,第二个参数指定了配置文件中对应的节点名称。
为了使用这些命名配置(Named Options), 我们需要在SlackNotificationService
类的构造函数中注入IOptionSnapshot
对象,而不是我们之前使用的IOption
对象。IOptionsSnapshot.Get(name)
方法允许我们通过传入唯一名称,获取对应的强类型配置对象。
public class SlackNotificationService
{
private readonly SlackApiSettings _devSettings;
private readonly SlackApiSettings _generalSettings;
private readonly SlackApiSettings _publicSettings;
public SlackNotificationService(IOptionsSnapshot<SlackApiSettings> options)
{
_devSettings = options.Get("Dev");
_generalSettings = options.Get("General");
_publicSettings = options.Get("Public");
}
}
这种方式最大的好处是,当添加新的Slack Channel时,你不需要添加任何新的配置类,你只需要针对新的Slack Channel配置一个新的SlackApiSetting
对象即可。缺点是从SlackNotificationService
的构造函数上,你已经不知道它对应的配置节点是哪个了。
总结
在本篇博客中,我们介绍了如何在ASP.NET Core中使用强类型配置。然后我们讨论了如何在ASP.NET Core的依赖注入容器中添加强类型配置对象的多个实例。这里我们讲解了使用3种不同的方式
- 创建父类配置对象
- 为每个Channel配置创建单独的配置类
- 使用Named Options
.NET Core 2.x中使用Named Options处理多个强类型配置实例的更多相关文章
- web工程中web.xml元素加载顺序以及配置实例
简介 web.xml是web工程的配置文件,容器加载web工程时,会首先从WEB-INF中查询web.xml,并加载其中的配置信息,可以将web.xml认为是web工程的入口. web.xml中包含有 ...
- 如何为ASP.NET Core的强类型配置对象添加验证
原文: Adding validation to strongly typed configuration objects in ASP.NET Core 作者: Andrew Lock 译文: La ...
- ASP.NET Core 选项模式源码学习Options Configure(一)
前言 ASP.NET Core 后我们的配置变得更加轻量级了,在ASP.NET Core中,配置模型得到了显著的扩展和增强,应用程序配置可以存储在多环境变量配置中,appsettings.json用户 ...
- 避免在ASP.NET Core 3.0中为启动类注入服务
本篇是如何升级到ASP.NET Core 3.0系列文章的第二篇. Part 1 - 将.NET Standard 2.0类库转换为.NET Core 3.0类库 Part 2 - IHostingE ...
- ASP.NET Core 1.0 中使用 Swagger 生成文档
github:https://github.com/domaindrivendev/Ahoy 之前文章有介绍在ASP.NET WebAPI 中使用Swagger生成文档,ASP.NET Core 1. ...
- Entity Framework Core 2.0 中使用LIKE 操作符
Entity Framework Core 2.0 中使用LIKE 操作符 不定时更新翻译系列,此系列更新毫无时间规律,文笔菜翻译菜求各位看官老爷们轻喷,如觉得我翻译有问题请挪步原博客地址 本博文翻译 ...
- 在ASP.NET Core 2.0中使用CookieAuthentication
在ASP.NET Core中关于Security有两个容易混淆的概念一个是Authentication(认证),一个是Authorization(授权).而前者是确定用户是谁的过程,后者是围绕着他们允 ...
- ASP.NET Core 2.1中基于角色的授权
ASP.NET Core 2.1中基于角色的授权 授权是来描述用户能够做什么的过程.例如,只允许管理员用户可以在电脑上进行软件的安装以及卸载.而非管理员用户只能使用软件而不能进行软件的安装以及卸载.它 ...
- ASP.NET Core 2.2中的Endpoint路由
Endpoint路由 在ASP.NET Core 2.2中,新增了一种路由,叫做Endpoint(终结点)路由.本文将以往的路由系统称为传统路由. 本文通过源码的方式介绍传统路由和Endpoint路由 ...
随机推荐
- 猎鱼达人_PC按键
更新模式[强制] 更新版本[3.13] 更新链接[https://yunfei-1256035889.cos.ap-beijing.myqcloud.com/%E6%8C%89%E9%94%AE%E7 ...
- Gatsby上手指南 - 让你的静态网站用react来高逼格的写
注意:Gatsby V2版本安装及使用问题请移步<Gastby V2安装过程中常见问题>,此文较旧,主要针对V1版Gatsby而介绍 前言 一直以来都是用之前比较流行的静态网站生成器Hex ...
- angularjs和jquery前端发送以http请求formdata数据
formdata是比较常见的前端发送给后端的请求,不仅可以上传数据,而且同时可以上传文件. jquery使用http请求上传formdata数据的方法: var formdata = new Form ...
- 更换MariaDB数据库
https://downloads.mariadb.org/mariadb/repositories/#mirror=neusoft&distro=Ubuntu&distro_rele ...
- vscode设置中文语言
https://jingyan.baidu.com/article/7e44095377c9d12fc1e2ef5b.html
- 用generator改写ajax
function request(url) { // this is where we're hiding the asynchronicity, // away from the main code ...
- C#找不到ConfigurationManager类
c#添加了Configuration;后,竟然找不到 ConfigurationManager 这个类,后来才发现:虽然引用了using System.Configuration;这个包,但是还是不行 ...
- 关于height、offsetheight、clientheight、scrollheight、innerheight、outerheight的区别
二.也是平时经常用到的offsetheight 它返回的高度是内容高+padding+边框,但是注意哦,木有加margin哦,当然一般也木有啥需要把margin加进去的,以上代码为例,结果显示上图h2 ...
- 谨以此篇献给DJANGO学习过程中遇到的问题
谨以此篇献给DJANGO学习过程中遇到的问题 一.Django数据同步过程中遇到的问题: 1.raise ImproperlyConfigured('mysqlclient 1.3.13 or new ...
- 关于AngularJS学习整理---核心特性
接触.学习AngularJS已经三个多月了,随着学习的深入,有些东西刚开始不明白,现在开始慢慢明白起来.于是,开始整理这几个月的学习成果.要不又要忘了... 初学Angular,是看到慕课网大漠穷秋 ...