配置的本质就是字符串的键值对,但是对于面向对象语言来说,能使用强类型的配置是何等的爽哉!

目录

  1. ASP.NET Core 配置系统
  2. 强类型的 Options
  3. Configure 方法
  4. ConfigureNamedOptions

ASP.NET Core 配置系统

在ASP.NET 4.X中,通常将配置存储在 web.config 中,使用静态帮助类来获取这些配置,而对 web.cofng 中进行任何修改时,则会导致应用程序池的回收,这种实现方式并不是很友好。

因此,在ASP.NET Core中,对配置系统进行了重写,仍然使用的是基本的键值对,但是它们可以从多种格式的配置源中来获取,比如:命令行、环境变量、XML文件、JSON文件等等,你也可以编写自定义的配置源提供程序。

通常在Stratup类的构造函数中对配置源进行设置:

public Startup(IHostingEnvironment env)
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
.AddEnvironmentVariables();
Configuration = builder.Build();
} public IConfigurationRoot Configuration { get; }

首先创建了一个ConfigurationBuilder,然后设置配置源,最终生成的Configuration是所有配置源中的键值对组合。

你可以直接在程序中使用IConfigurationRoot来读取配置,但是建议你使用强类型的Options,这样在你想获取某个配置时,只需要注入对应的Options,而不是获取整个配置。

强类型的 Options

Options is a framework for accessing and configuring POCO settings.

简单来说,Options 就是将一个 POCO 的配置类,通过在Startup类中注册到容器中,在后续使用的时候使用构造函数注入来获取到POCO对象。我们将这种编程模式称为Options模式

首先定义一个 Options

public class MyOptions
{
public string DefaultValue { get; set; }
}

然后我们在对应的appsettings.json中添加如下片段:

{
"MyOptions": {
"DefaultValue" : "first"
}
}

Startup中的ConfigureServices方法中,进行服务的注册:

public void ConfigureServices(IServiceCollection services)
{
services.Configure<MyOptions>(Configuration.GetSection("MyOptions"));
}

最后,便在控制器中注入IOptions<MyOptions>,通过其Value属性对MyOptions进行访问:

[Route("api/[controller]")]
public class ValuesController : Controller
{
private readonly MyOptions _options;
public ValuesController(IOptions<MyOptions> options)
{
_options = options.Value;
} [HttpGet]
public string Get()
{
return _options.DefaultValue;
}
}

Configure 方法

Options框架为我们提供了一系统的IServiceCollection的扩展方法,方便我们的使用。

直接使用Action配置

// 最简单的注册方式
services.Configure<MyOptions>(o => o.DefaultValue = true); // 指定具体名称
services.Configure<MyOptions>("my", o => o.DefaultValue = true); // 配置所有实例
services.ConfigureAll<MyOptions>(o => o.DefaultValue = true);

通过配置文件进行配置

// 使用配置文件来注册实例
services.Configure<MyOptions>(Configuration.GetSection("Sign")); // 指定具体名称
services.Configure<MyOptions>("my", Configuration.GetSection("Sign")); // 配置所有实例
services.ConfigureAll<MyOptions>(Configuration.GetSection("Sign"));

PostConfigure方法

PostConfigure 方法在 Configure 方法之后执行,是2.0中新增加的。

services.PostConfigure<MyOptions>(o => o.DefaultValue = true);
services.PostConfigure<MyOptions>("smyign", o => o.DefaultValue = true);
services.PostConfigureAll<MyOptions>(o => o.DefaultValue = true);

源码解析

首先看一下IConfigureOptions接口:

public interface IConfigureOptions<in TOptions> where TOptions : class
{
void Configure(TOptions options);
}

Configure扩展方法中便是为IConfigureOptions<>注册了一个单例ConfigureNamedOptions<>

public static IServiceCollection Configure<TOptions>(this IServiceCollection services, string name, Action<TOptions> configureOptions)
where TOptions : class
{
services.AddOptions();
services.AddSingleton<IConfigureOptions<TOptions>>(new ConfigureNamedOptions<TOptions>(name, configureOptions));
return services;
}

而不指定nameConfigureConfigureAll方法,都只是一种简写形式,使用默认的DefaultName

public static class Options
{
public static readonly string DefaultName = string.Empty;
} public static IServiceCollection Configure<TOptions>(this IServiceCollection services, Action<TOptions> configureOptions) where TOptions : class
=> services.Configure(Options.Options.DefaultName, configureOptions); public static IServiceCollection ConfigureAll<TOptions>(this IServiceCollection services, Action<TOptions> configureOptions) where TOptions : class
=> services.Configure(name: null, configureOptions: configureOptions);

如上,Configure方法其实就是为IConfigureOptions<>注册了一个单例ConfigureNamedOptions<>

再看一下使用IConfiguration进行配置的扩展方法:

public static IServiceCollection Configure<TOptions>(this IServiceCollection services, string name, IConfiguration config)
where TOptions : class
{
services.AddOptions();
services.AddSingleton<IOptionsChangeTokenSource<TOptions>>(new ConfigurationChangeTokenSource<TOptions>(name, config));
return services.AddSingleton<IConfigureOptions<TOptions>>(new NamedConfigureFromConfigurationOptions<TOptions>(name, config));
}

可以看到,注册的实例变成了NamedConfigureFromConfigurationOptions,而其本质依然是调用ConfigureNamedOptions,只不过Action的方法体变成了ConfigurationBinder.Bind()

public class NamedConfigureFromConfigurationOptions<TOptions> : ConfigureNamedOptions<TOptions>
where TOptions : class
{
public NamedConfigureFromConfigurationOptions(string name, IConfiguration config)
: base(name, options => ConfigurationBinder.Bind(config, options))
{
if (config == null)
{
throw new ArgumentNullException(nameof(config));
}
}
}

在上面的Configure方法中,都调用了AddOptions,我们来看一下:

public static IServiceCollection AddOptions(this IServiceCollection services)
{
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<>)));
return services;
}

如上便是Options系统的几个核心对象了,后续章节再来一一介绍。

大家或许会有点疑问:“ IConfigureOptions 中的 Configure 方法是在什么时候调用的呢 ”,这个且看下章分解。

ConfigureNamedOptions

ConfigureNamedOptions 实现了 IConfigureNamedOptions,而 IConfigureNamedOptions 则是对 IConfigureOptions 的一个扩展,添加了Name参数,这样,我们便可以为同一个 Options 类型注册多个独立的实例,在某些场景下则是非常有用的。有关Name的使用,则会在 第三章 来讲。

public interface IConfigureNamedOptions<in TOptions> : IConfigureOptions<TOptions> where TOptions : class
{
void Configure(string name, TOptions options);
}

再看一下 ConfigureNamedOptions 的源码:

public class ConfigureNamedOptions<TOptions> : IConfigureNamedOptions<TOptions>, IConfigureOptions<TOptions> where TOptions : class
{
public ConfigureNamedOptions(string name, Action<TOptions> action)
{
Name = name;
Action = action;
} public string Name { get; } public Action<TOptions> Action { get; } public virtual void Configure(string name, TOptions options)
{
if (Name == null || name == Name)
{
Action?.Invoke(options);
}
} public void Configure(TOptions options) => Configure(Options.DefaultName, options);
}

ConfigureNamedOptions 本质上就是把我们注册的Action包装成统一的Configure方法,以方便后续创建Options实例时,进行初始化。

总结

本文描述了在 .NET Core 配置系统中Options的配置及原理,在 下一章 来讲一下IOptions的使用。

ASP.NET Core 2.1 源码学习之 Options[1]:Configure的更多相关文章

  1. ASP.NET Core 2.1 源码学习之 Options[1]:Configure 【转】

    原文链接:https://www.cnblogs.com/RainingNight/p/strongly-typed-options-configure-in-asp-net-core.html 配置 ...

  2. ASP.NET Core 2.1 源码学习之 Options[3]:IOptionsMonitor

    前面我们讲到 IOptions 和 IOptionsSnapshot,他们两个最大的区别便是前者注册的是单例模式,后者注册的是 Scope 模式.而 IOptionsMonitor 则要求配置源必须是 ...

  3. ASP.NET Core 2.1 源码学习之 Options[3]:IOptionsMonitor 【转】

    原文链接:https://www.cnblogs.com/RainingNight/p/strongly-typed-options-ioptions-monitor-in-asp-net-core. ...

  4. ASP.NET Core 2.1 源码学习之 Options[2]:IOptions

    在 上一章 中,介绍了Options的注册,而在使用时只需要注入 IOption<T> 即可: public ValuesController(IOptions<MyOptions& ...

  5. ASP.NET Core 2.1 源码学习之 Options[2]:IOptions 【转】

    原文链接:https://www.cnblogs.com/RainingNight/p/strongly-typed-options-ioptions-in-asp-net-core.html 在 上 ...

  6. ASP.NET Core 源码学习之 Options[1]:Configure

    配置的本质就是字符串的键值对,但是对于面向对象语言来说,能使用强类型的配置是何等的爽哉! 目录 ASP.NET Core 配置系统 强类型的 Options Configure 方法 源码解析 ASP ...

  7. ASP.NET Core 选项模式源码学习Options Configure(一)

    前言 ASP.NET Core 后我们的配置变得更加轻量级了,在ASP.NET Core中,配置模型得到了显著的扩展和增强,应用程序配置可以存储在多环境变量配置中,appsettings.json用户 ...

  8. ASP.NET Core 选项模式源码学习Options IOptions(二)

    前言 上一篇文章介绍IOptions的注册,本章我们继续往下看 IOptions IOptions是一个接口里面只有一个Values属性,该接口通过OptionsManager实现 public in ...

  9. ASP.NET Core 选项模式源码学习Options IOptionsMonitor(三)

    前言 IOptionsMonitor 是一种单一示例服务,可随时检索当前选项值,这在单一实例依赖项中尤其有用.IOptionsMonitor用于检索选项并管理TOption实例的选项通知, IOpti ...

随机推荐

  1. react-fetch数据发送请求

    在一个项目中,数据的请求发送数据是最为重要的,不可能我们的数据都是自己进行编写的 在react中官方推荐使用的方法是fetch.当然它里面也可以使用vue中的axios请求数据,jQuery的$.aj ...

  2. Testlink1.9.17使用方法(第八章 测试执行/报告BUG)

    第八章 测试执行/报告BUG QQ群交流:585499566 把他们放到一起,是因为报告bug是在执行的过程中同步进行的——即执行用例的过程中一旦发现bug我们需要立即把其报告到我们的bug管理系统r ...

  3. AS插件-GsonFormat

    支持 field 类型的修改. 支持快捷键打开 GsonFormat ,默认为 option+s(mac), alt+s(win) 支持 field 名称的修改. 支持添加 field 前缀. 支持多 ...

  4. ExtJS中layout的12种布局风格

    总览 extjs的容器组件都可以设置它的显示风格,它的有效值有 1. absolute,2. accordion, 3. anchor, 4. border, 5. card, 6. column, ...

  5. mysql练习----Self join

    stops(id, name) route(num,company,pos, stop)   stops route id num name company   pos   stop    

  6. c# 采用datatable 快速导入数据至MSSQL的方法分享

    转自:http://www.maomao365.com/?p=5613 摘要:下文讲述使用c#代码快速将dataTable导入至mssql数据库的方法 实现思路:需要将datatable调整为同目标表 ...

  7. c/c++ 标准库 map set 插入

    标准库 map set 插入 一,插入操作 有map如下: map<string, size_t> cnt; 插入方法: 插入操作种类 功能描述 cnt.insert({"abc ...

  8. 创建一个C++制作的包含Opencv功能的dll,供C#程序使用

    目的:获取某图片指定位置的颜色. 实现该目的的方法有很多,但为了有助于扩充自己技术广度,所以决定采用标题中的方法来完成. 没有C++编程经验,也没有制作C++版Opencv语法经验,也没有制作dll的 ...

  9. C# → 数据库

    Database: public static class dbconnection{ static string cons = "data source = (local) ;initia ...

  10. echarts设置y轴值间隔

    其中min.max可以自定义可以动态获取数据 yAxis : [ {                        type : 'value',                        axi ...