ASP.NET Core - 选型系统之选型配置
1. 选项
前面讲完了.NET Core 下的配置系统,我们可以通过 IConfiguration 服务从各种来源的配置中读取到配置信息,但是每次要用的时候都通过 Iconfiguration 读取配置文件会比较不方便,而且效率低。.NET Core 体系下提供了一个选项系统,该功能用于实现以强类型的方式对程序配置信息进行访问,并且可以将选项类注入到依赖注入容器中进行管理和使用。
在进行配置信息的强类型选项绑定的时候,需要一个相应的选项类,该类推荐按 {Object}Options 命名规则进行命名,有以下特点:
- 必须非抽象类
- 必须包含公共无参的构造函数
- 类中需要与配置项进行绑定的属性必须拥有 public 的 get、set 访问器,并且属性的命名必须与配置键一直,不区分大小写
- 要确保配置项能够转换到其绑定的属性类型
- 该类的字段不会被绑定
2. 选项配置方式
2.1 手动绑定
IConfiguration 服务通过 ConfigurationBinder 类扩展了 Get 和 Bind 两个方法,这两个方法可以将配置信息绑定到选项类上。这种方式在之前的 ASP.NET Core - 配置系统之配置读取 一章有讲到过,这里再做一下演示:
首先在配置文件中添加以下节点:
"Blog": {
"Title": "ASP.NET Core Options",
"Content": "This is a blog about Options System in ASP.NET Core Framework.",
"CreateTime": "2022-12-06"
}
之后定义一个选项类:
public class BlogOptions
{
public const string Blog = "Blog";
public string Title { get; set; }
public string Content { get; set; }
public DateTime CreateTime { get; set; }
}
然后,在任何可以获取到 IConfiguration 服务的地方都可以通过 IConfiguration 服务进行绑定:
using OptionsSample.Options;
using System.Text.Json;
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
var blog1 = new BlogOptions();
app.Configuration.GetSection(BlogOptions.Blog).Bind(blog1);
var blog2 = app.Configuration.GetSection(BlogOptions.Blog).Get<BlogOptions>();
Console.WriteLine(JsonSerializer.Serialize(blog1));
Console.WriteLine(JsonSerializer.Serialize(blog2));
app.Run();

这种方式依旧有些不方便,虽然也有好处,能够监测到配置的修改,在应用运行中同步改变(如果相应的配置处理程序支持变更重载的化),但每次都要指定相应的节点,每次都要实时构建新的选项对象,并且选项系统也能做到配置更改时更新选项。
2.2 依赖注入配置
2.2.1 配置文件节点转换选项
除了手动绑定的方式外,我们还可以在应用启动的时候读取相应的配置节点,配置成Options并同时注册到依赖注入容器中,由依赖注入容器管理其生命周期,并且多次使用。我们可以像注册其他的依赖注入关系一样,在入口文件中通过 IServiceCollection 的 Configure 扩展方法进行配置。
// 通过配置文件读取某一配置节点
//builder.Services.Configure<BlogOptions>(builder.Configuration.GetSection(BlogOptions.Blog));
这里通过获取特定的配置节点,并将其配置为选项,之后就可以在任何能够进行依赖注入的地方使用注入的 BlogOptions 选项类了。
var blogOption = app.Services.GetRequiredService<IOptions<BlogOptions>>().Value;
Console.WriteLine(JsonSerializer.Serialize(blogOption));
这里使用到的 Configure 方法是 OptionsConfigurationServiceCollectionExtensions 类中的扩展方法,选项系统中有好几个同名的 Configure 方法,但是这些是并不是同一个方法的重载,下面会详细讲到。
2.2.2 硬编码配置选项
除了从配置文件中读取配置节点转换为选项之外,我们也可以直接在代码中硬编码指定选项内容,并注入到容器之中。
//硬编码的方式设置配置信息,也可以在这里读取数据库信息
builder.Services.Configure<BlogOptions>(option =>
{
option.Title = "test";
option.Content = "test hard code option";
option.CreateTime = DateTime.Now;
});
这种情况用得不多,在这种情况下我们可以进行额外的一些逻辑,例如从数据库中获取一些信息。这里的 Configure 方法是 OptionsServiceCollectionExtensions 扩展类中的方法。值得注意的是,在同时使用上面两个方法配置同一个选项类的情况下,硬编码配置的方式优先。
2.2.3 使用DI服务配置选项
在某些场景下,选项的配置需要比较复杂的逻辑,会依赖容器中的其他服务,我们还可以使用以下方式:
builder.Services.AddOptions<BlogOptions>()
// 这里可以通过 Configure 方法指定需要的服务, IServiceProvider 只是一个示例
.Configure<IServiceProvider>((option, service) => // 接收的的第一个参数选项类对象,后面依次是所注入的服务
{
// 通过注入的服务执行相应的逻辑
option.Title = "test DI Configure";
option.Content = "test DI Configure";
option.CreateTime = DateTime.Now;
});
这里的 Configure 方法,与上面的不一样,不再是 IServiceCollection 的扩展方法,而是 OptionsBuilder 类中的方法,AddOptions 扩展方法在 OptionsServiceCollectionExtensions 中,返回一个 OptionsBuilder 对象。该方法有多个重载,最多支持5个服务来配置选项:

当使用这种方式和上面的硬编码的方式同时配置同一个选项类的情况下,哪部分代码后执行,最后选项类的内容就以哪部分为准。
2.2.4 命名选项
在一些情况下,应用中是存在多份配置结构相同,但具体配置值不同的配置信息的,例如以下的情况:
"FirstBlog": {
"Title": "ASP.NET Core Options",
"Content": "This is a blog about Options System in ASP.NET Core Framework.",
"CreateTime": "2022-12-06"
},
"SecondBlog": {
"Title": "ASP.NET Core Configuration",
"Content": "This is a blog about Configuration System in ASP.NET Core Framework.",
"CreateTime": "2022-12-08"
}
这种情况下,两个配置节点其实可以用同一个选项类接收,它们的结构是一样的。而命名选项就是为了应对这种情况,选项系统中允许为当前配置的选项命名,而选项系统区分同一个类型的不同选项也是根据名字进行区分的。事实上,.NET Core 中所有 Options 都是命名选项,当没有显式指定名字时(如上面讲到的默认配置方式),使用的名字默认是Options.DefaultName,即string.Empty。
builder.Services.Configure<BlogOptions>("First", builder.Configuration.GetSection("FirstBlog"));
builder.Services.Configure<BlogOptions>("Second", builder.Configuration.GetSection("SecondBlog"));
这里要说明的是,上面我们从依赖注入容器中解析 IOptions 接口,从而获取我们需要的选项值,但是使用命名选项的情况下是无法通过 IOptions 接口解析相应的选项的,必须通过 IOptionsMonitor 或者 IOptionsSnapshot 接口来解析。这三个接口的区别下面会重点讲。
var blog = app.Services.GetRequiredService<IOptions<BlogOptions>>().Value;
Console.WriteLine(JsonSerializer.Serialize(blog));
var firstBlog = app.Services.GetRequiredService<IOptionsMonitor<BlogOptions>>().Get("First");
Console.WriteLine(JsonSerializer.Serialize(firstBlog));
var secondBlog = app.Services.GetRequiredService<IOptionsMonitor<BlogOptions>>().Get("Second");
Console.WriteLine(JsonSerializer.Serialize(secondBlog));

2.2.4 后期配置
后期配置是指当我们通过前面的方法对一个选项类进行配置之后,可能还会因为其他业务逻辑需要对其中的配置信息进行修改,这时候我们可以通过后期配置对选项系统中已配置的选项内容进行替换。后期配置在所有的OptionsServiceCollectionExtensions.Configure 执行完成之后执行,即配置系统不管代码顺序,会先完成所有选项的配置,再执行后期配置。
builder.Services.Configure<BlogOptions>("First", builder.Configuration.GetSection("FirstBlog"));
builder.Services.PostConfigure<BlogOptions>("First", options =>
{
options.Title = "Post Config";
});
var firstBlog = app.Services.GetRequiredService<IOptionsMonitor<BlogOptions>>().Get("First");
Console.WriteLine(JsonSerializer.Serialize(firstBlog));

除了 PostConfigure 方法外,还有 PostConfigureAll 方法,前者对单一选项类单一命名的选项对象进行后期配置,如果没有指定名称则是默认名称,后者会对统一选项类下的不同命名选项统一进行配置。
builder.Services.Configure<BlogOptions>("First", builder.Configuration.GetSection("FirstBlog"));
builder.Services.Configure<BlogOptions>("Second", builder.Configuration.GetSection("SecondBlog"));
builder.Services.PostConfigureAll<BlogOptions>(options =>
{
options.Title = "Post Config";
});
var firstBlog = app.Services.GetRequiredService<IOptionsMonitor<BlogOptions>>().Get("First");
Console.WriteLine(JsonSerializer.Serialize(firstBlog));
var secondBlog = app.Services.GetRequiredService<IOptionsMonitor<BlogOptions>>().Get("Second");
Console.WriteLine(JsonSerializer.Serialize(secondBlog));

通过 IOptionsMonitor 或者 IOptionsSnapshot 接口解析选项,在配置来源更改的情况下,相应的选项内容也会随之变化(两者的适用场景不同), 后期配置逻辑在选项的每一次更改之后都会执行。
参考文章:
ASP.NET Core 中的选项模式 | Microsoft Learn
选项模式 - .NET | Microsoft Learn
面向 .NET 库创建者的选项模式指南 - .NET | Microsoft Learn
理解ASP.NET Core - 选项(Options)
ASP.NET Core 系列:
目录:ASP.NET Core 系列总结
上一篇:ASP.NET Core - 配置系统之自定义配置提供程序
ASP.NET Core - 选型系统之选型配置的更多相关文章
- Linux CentOS7部署ASP.NET Core应用程序,并配置Nginx反向代理服务器
前言: 本篇文章主要讲解的是如何在Linux CentOS7操作系统搭建.NET Core运行环境并发布ASP.NET Core应用程序,以及配置Nginx反向代理服务器.因为公司的项目一直都是托管在 ...
- asp.net core 教程(五)-配置
Asp.Net Core-配置 Asp.Net Core-配置 在这一章,我们将讨论 ASP.NET Core项目的相关的配置.在解决方案资源管理器中,您将看到 Startup.cs 文件.如果你有以 ...
- asp.net core重新加载应用配置
asp.net core重新加载应用配置 Intro 我把配置放在了数据库或者是Redis里,配置需要修改的时候我要直接修改数据库,然后调用一个接口去重新加载应用配置,于是就尝试写一个运行时重新加载配 ...
- ASP.NET Core应用程序的参数配置及使用(转载)
本文结构 提前准备 参数配置方式 appsettings.json 环境变量 命令行参数 在控制器中使用配置参数 注入IConfiguration对象 注入IOptions对象 总结 应用程序的开发不 ...
- .NET 黑魔法 - asp.net core 日志系统
asp.net core 里如何记录日志呢? 这要从asp.net core的依赖注入说起,在asp.net core里的依赖注入真是无所不在,各种面向切面的接口与事件. 好吧,来点干货. 首先,我们 ...
- ASP.NET Core 1.0 基础之配置
来源https://docs.asp.net/en/latest/fundamentals/configuration.html ASP.NET Core 1.0支持不同的配置选项.应用配置数据可以是 ...
- CZGL.Auth: ASP.NET Core Jwt角色授权快速配置库
CZGL.Auth CZGL.Auth 是一个基于 Jwt 实现的快速角色授权库,ASP.Net Core 的 Identity 默认的授权是 Cookie.而 Jwt 授权只提供了基础实现和接口,需 ...
- ASP.NET Core 在 JSON 文件中配置依赖注入
前言 在上一篇文章中写了如何在MVC中配置全局路由前缀,今天给大家介绍一下如何在在 json 文件中配置依赖注入. 在以前的 ASP.NET 4+ (MVC,Web Api,Owin,SingalR等 ...
- ASP.NET Core 注入和获取 AppSettings 配置
ASP.NET Core 项目中有个appsettings.json配置文件,用于存放一些配置信息,比如数据库连接字符串等,但访问的话,只能在 ASP.NET Core 项目中获取,如果我们在其他项目 ...
- asp.net core 2.0中的配置(1)---Configuration
配置就是一个装配数据字典的过程,一个字典也就是一个键值对,所以从配置就是键值对. 在asp.net core中关于配置是由四个基本的类型来支撑的,是①IConfigurationSource②ICon ...
随机推荐
- pythonn全栈学习笔记--logging模块学习(四)
一.logging相关配置 1 import logging 2 """ 3 asctime:运行时间 4 name:主模块名称 5 levelname:日志级别 INF ...
- javascript向tabale中动态添加数据
<table width="600" border="1" cellspacing="0"> <thead> < ...
- bzoj 2337
有人说这题像游走... 关于游走的思想,他死了... 明明直接从期望dp的角度考虑更简单合理嘛 首先由于是异或运算不妨逐位考虑 对于每一位,设状态$f[i]$表示从第$i$个点到第$n$个点,这一位上 ...
- MySQL Atlas 读写分离软件介绍
MySQL Atlas介绍 目录 MySQL Atlas介绍 一.MySQL Atlas介绍 1.1.1 MySQL Atlas介绍 1.1.2 Atlas基本管理 一.MySQL Atlas介绍 1 ...
- leetcode之——二分法模板
class Solution: def search(self, nums: List[int], target: int) -> int: n=len(nums) left,right=0,n ...
- obspy常用命令记录
如何使用obspy(适用于MAC.Linux.Windows)完全替代SAC(使用于Linux和MAC) 波形预处理 # 去均值 stream.detrend('demean') # 去线性趋势 st ...
- python3GUI--仿做一个网易云音乐By:PyQt5(附下载地址)
@ 目录 一.前言 二.展示-主界面 1.静图1 2.静图2 3.静图3 3.静图3 4.动图1 三.展示-登录界面 1.静图1 2.静图2 5.动图2 四.展示-系统托盘 五.UI设计记录 1.UI ...
- 高并发解决方案之 redis原子操作(适用于秒杀场景)
秒杀活动: 秒杀场景一般会在电商网站或(APP/小程序)举行一些活动或者节假日在12306网站上抢票时遇到.对于一些稀缺或者特价商品,一般会在约定时间点对其进行限量销售,因为这些商品的特殊性,会吸引大 ...
- api接口基础Day1
精华笔记: String: String的常用方法: length():获取字符串的长度(字符个数) trim():去除当前字符串两边的空白字符 toUpperCase()/toLowerCase() ...
- 当MYSQL报错时
输入mysqld --console查看错误 针对error行进行排查