咱们继续上一个话题。先简单复习一下,根据老周前面文章的介绍,选项类体系的基本套路是通过 IOptionsFactory 来创建选项类实例的。而我们在服务容器(IServiceCollection)上是用Configure、PostConfigure 等扩展方法去配置选项类的(设置属性的值)。配置代码并不是立即执行,而是通过委托来让写代码的人自己设定属性值,最后向服务容器添加 IConfigureOptions、 IPostConfigureOptions、IValidateOptions 等关联服务。IOptionsFactory 通过依赖注入获得上述服务,并使用它们来设置选项类。

本文重点说一下选项类的依赖注入——如何注入到其他类型中。这个当然是依靠构造函数(使用 Invoke 约定的中间件类除外)了(GetService、GetRequiredService 等方法也可以)。选项类自身不是直接添加到服务容器中,所以不能用于依赖注入。

我们还得回过头去看一下 AddOptions 扩展方法的源代码。

   services.TryAdd(ServiceDescriptor.Singleton(typeof(IOptions<>), typeof(UnnamedOptionsManager<>)));
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<>)));

其他的忽略不谈,我们一般用得多的是这三个:

IOptions:单例模式,全场只创建一个实例。这个适用于只想读选项类信息的情形。

IOptionsSnapshot:作用域模式,生命周期和一次HTTP请求相同。这个适用于选项有分组命名的情况,就是同一个选项类有多组设置时。

IOptionsMonitor:这个也是单实例模式。既可用于单组选项类也可用于多组选项类。当与选项类绑定的配置(通常是配置文件,如 appsettings.json)更新时会自动产生通知。此时不用重启应用程序,刷新一下页面就能获取到新的信息。

接下来就是实战阶段,咱们先准备一个选项类。

    public class DemoOptions
{
public string? AppTitle { get; set; }
public uint MaxInstance { get; set; }
public bool Locked { get; set; }
}

这个选项类仅用于演示,我是随便写的,没特殊含义,请不要恶意猜测其实际用途。

接下来就很简单了。在应用程序初始化时咱们配置一下。

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers(); // MVC无V
// 配置选项类
builder.Services.Configure<DemoOptions>(opt =>
{
opt.AppTitle = "山高皇帝远,坏事干翻天";
opt.MaxInstance = 12;
opt.Locked = false;
});
var app = builder.Build();
// 添加 MVC 路由
app.MapControllerRoute("Lily", "{controller=Main}/{action=Work}"); app.Run();

随后,咱们定义个控制器类,就可以用了。

    public class MainController : Controller
{
// 字段
readonly IOptions<DemoOptions> _myOpt; // 构造函数接收赏赐
public MainController(IOptions<DemoOptions> o)
{
_myOpt = o;
} // 操作方法
public ActionResult Work()
{
DemoOptions theOption = _myOpt.Value;
string s = "App Title: " + theOption.AppTitle +
$"\nMax Instance: {theOption.MaxInstance}\n" +
$"Locked: {theOption.Locked}";
return Content(s);
}
}

运行后,如果看到以下内容,说明你的代码没写错。

如果,选项有不同的分组。比如叫 g1 和 g2。

builder.Services.Configure<DemoOptions>("g1", opt =>
{
opt.AppTitle = "鸭梨山小";
opt.MaxInstance = 5;
opt.Locked = true;
});
builder.Services.Configure<DemoOptions>("g2", opt =>
{
opt.AppTitle = "无头公鸡";
opt.MaxInstance = 15;
opt.Locked = false;
});

其实,未分组的选项类也是有一个默认组名的,叫空白字符串(string.Empty),可从 Options.DefaultName 字段获取。

选项分组后,同一个选项类就拥有不同的配置方案。

接下来的使用也不复杂。

    public class GpOptController : Controller
{
// 字段
IOptionsSnapshot<DemoOptions> _myOpt;
// 构造函数接受封赏
public GpOptController(IOptionsSnapshot<DemoOptions> opt)
{
_myOpt = opt;
} // 操作方法
// g 是分组名称
public ActionResult GetInfo(string g)
{
DemoOptions opt = _myOpt.Get(g);
return Content(
$"App Title: {opt.AppTitle}\n" +
$"Max Instance: {opt.MaxInstance}\n" +
$"Locked: {opt.Locked}"
);
}
}

注意 GetInfo 方法有个参数 g,用来筛选显示哪个分组的选项类(g1?g2?)。

假如要显示 g1 的选项信息,就请求 http://SBHost:7337/gpopt/getinfo?g=g1。得到结果如下。

同理,g2 的访问URL:https://BugHost/gpopt/getinfo?g=g2。结果如下。

不要访问什么 g3、g4 的,因为没有这些分组,将得到一个带默认属性值的 DemoOptions 实例。

最后,我们来看看 IOptionsMonitor。要想让 IOptionsMonitor 在关联的配置更改时获得更新通知,还需要实现 IOptionsChangeTokenSource,然后将其添加到服务容器中。内部默认实现的类是 ConfigurationChangeTokenSource。当配置文件被更改后会让 IOptionsMonitor 得到通知。实现 IOptionsChangeTokenSource 接口似乎有些复杂,一般我们不需要这样做。

接下来我们把前面的代码改一下,在应用程序初始化时,用 appsettings.json 文件中的内容去配置选项类。

builder.Services.Configure<DemoOptions>(builder.Configuration.GetSection("test"));

配置 DemoOptions 类的节点名为“test”。打开 appsettings.json 文件,加上这个节点。

{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"test": {
"appTitle": "纸糊的飞机",
"maxInstance": 16,
"locked": true

}

}

定义一个控制器类,用于获取选项类信息。

    public class WKController : Controller
{
// 字段
readonly IOptionsMonitor<DemoOptions> _mntOpt;
// 构造函数接受册封
public WKController(IOptionsMonitor<DemoOptions> o)
{
_mntOpt = o;
} // 操作方法
public ActionResult Do()
{
DemoOptions opt = _mntOpt.CurrentValue;
return Content(
$"App Title: {opt.AppTitle}\n" +
$"Max Instance: {opt.MaxInstance}\n" +
$"Locked: {opt.Locked}"
);
}
}

运行应用程序,定位到URL:https://localhost/wk/do,得到如下结果。

不用关闭应用程序,打开 appsettings.json 文件,把它改一下。

  "test": {
"appTitle": "茶叶青",
"maxInstance": 36,
"locked": true
}

然后保存文件,回面 web 页面,刷新一下,就能看到新的配置了。

应用程序在不重启的情况下加载最新的选项配置。

今天的水文到此就结束了,咱们下次再聊。

【ASP.NET Core】选项类的依赖注入的更多相关文章

  1. ASP.NET Core中如影随形的”依赖注入”[下]: 历数依赖注入的N种玩法

    在对ASP.NET Core管道中关于依赖注入的两个核心对象(ServiceCollection和ServiceProvider)有了足够的认识之后,我们将关注的目光转移到编程层面.在ASP.NET ...

  2. [ASP.NET Core 3框架揭秘] 依赖注入:控制反转

    ASP.NET Core框架建立在一些核心的基础框架之上,这些基础框架包括依赖注入.文件系统.配置选项和诊断日志等.这些框架不仅仅是支撑ASP.NET Core框架的基础,我们在进行应用开发的时候同样 ...

  3. [ASP.NET Core 3框架揭秘] 依赖注入[5]: 利用容器提供服务

    毫不夸张地说,整个ASP.NET Core框架是建立在依赖注入框架之上的.ASP.NET Core应用在启动时构建管道以及利用该管道处理每个请求过程中使用到的服务对象均来源于依赖注入容器.该依赖注入容 ...

  4. [ASP.NET Core 3框架揭秘] 依赖注入[8]:服务实例的生命周期

    生命周期决定了IServiceProvider对象采用怎样的方式提供和释放服务实例.虽然不同版本的依赖注入框架针对服务实例的生命周期管理采用了不同的实现,但总的来说原理还是类似的.在我们提供的依赖注入 ...

  5. [ASP.NET Core 3框架揭秘] 依赖注入[10]:与第三方依赖注入框架的适配

    .NET Core具有一个承载(Hosting)系统,承载需要在后台长时间运行的服务,一个ASP.NET Core应用仅仅是该系统承载的一种服务而已.承载系统总是采用依赖注入的方式来消费它在服务承载过 ...

  6. [ASP.NET Core 3框架揭秘] 依赖注入[9]:实现概述

    <服务注册>.<服务消费>和<生命周期>主要从实现原理的角度对.NET Core的依赖注入框架进行了介绍,接下来更进一步,看看该框架的总体设计和实现.在过去的多个版 ...

  7. [ASP.NET Core 3框架揭秘] 依赖注入[7]:服务消费

    包含服务注册信息的IServiceCollection集合最终被用来创建作为依赖注入容器的IServiceProvider对象.当需要消费某个服务实例的时候,我们只需要指定服务类型调用IService ...

  8. [ASP.NET Core 3框架揭秘] 依赖注入[6]:服务注册

    通过<利用容器提供服务>我们知道作为依赖注入容器的IServiceProvider对象是通过调用IServiceCollection接口的扩展方法BuildServiceProvider创 ...

  9. [ASP.NET Core 3框架揭秘] 依赖注入[4]:一个Mini版的依赖注入框架

    在前面的章节中,我们从纯理论的角度对依赖注入进行了深入论述,我们接下来会对.NET Core依赖注入框架进行单独介绍.为了让读者朋友能够更好地理解.NET Core依赖注入框架的设计与实现,我们按照类 ...

随机推荐

  1. mySql in 语句查询优化

    有这么一条sql UPDATE product set BuyerCount =BuyerCount+1 where ProductId in( SELECT ProductId from order ...

  2. 最新版2022年任我行管家婆工贸版ERP M7 V22.0进销存财务生产管理软件网络版——云上的集团化制造管理系统

    在互联网+制造业的时代背景下,制造业在利用互联网技术进行转型升级的同时,也面临着供应链体系和生产模式的重塑,主要呈现出以下特点: 多元化发展 对外,传统企业正在通过"互联网+"逐步 ...

  3. 提升站点SEO的7个建议

    1.使用HTTPS 谷歌曾发公告表示,使用安全加密协议(HTTPS),是搜索引擎排名的一项参考因素. 所以,在域名相同情况下,HTTPS站点比HTTP站点,能获得更好的排名. 在网络渠道分发或合作上, ...

  4. 1.Spring开发环境搭建——intellj

    1.在intellj中新建项目,选择JDK版本(1.8版本) 2.选择相关信息填写,注意Java版本要和上面步骤选择的版本一致. 3.选择springBoot版本,勾选Spring Web选项. 4. ...

  5. Springboot启动类及注解说明

    Spring boot的启动是基于main方法的,其主要注解为: 1. @springBootApplication:项目的启动注解,是一个组合注解,包含@SpringbootConfiguratio ...

  6. Spring 中 @EnableXXX 注解的套路

    前言 在 Spring 框架中有很多实用的功能,不需要写大量的配置代码,只需添加几个注解即可开启. 其中一个重要原因是那些 @EnableXXX 注解,它可以让你通过在配置类加上简单的注解来快速地开启 ...

  7. 有趣的BUG之Stack Overflow

    今天遇到一个很有意思的bug,当程序开发完成后打包到服务器运行,总是会出现栈溢出异常,经过排查发现,问题出现在一个接口上,但这个接口逻辑并不复杂,除了几局逻辑代码外和打印语句之外也没有其他的了,但是只 ...

  8. Map和WeakMap的方法和区别

    Map Map是一组键值对的结构,具有极快的查找速度. 一.构造函数不同 let map = new Map(); let weakmap = new WeakMap(); 二.内置函数不同 Map的 ...

  9. lnav-日志查看器

    lnav是一个基于控制台的高级lnav是一个基于控制台的高级日志文件查看器(浏览器). lnav支持日志高亮显示内容以及查看压缩的日志文件,而且它可以使用较小的内存实时查看较大的日志文件.日志文件查看 ...

  10. 英语资源及其APP推荐

    step1:记单词 a,说到背单词常规方法是拿着一本单词书一个一个往下背.该种方法不仅枯燥且效率极低. b,app辅助记忆.在此就我用过的两个app做简述.第一个是百词斩 百词斩:功能主打图片记忆,并 ...