前言

配置是我们必不可少的功能,我们在开发中,经常会遇到需要获取配置信息的需求,那么如何才能优雅的获取配置信息?

我们希望新的配置:

  1. 支持强类型
  2. 配置变更后通知
  3. 学习难度低

快速入门

根据使用场景我们将配置分为本地配置以及远程配置,下面我们就来看一下本地配置与远程配置是如何来使用的?

本地配置

  1. 新建ASP.NET Core 空项目Assignment.MasaConfiguration,并安装Masa.Contrib.Configuration
dotnet new web -o Assignment.MasaConfiguration
cd Assignment.MasaConfiguration
dotnet add package Masa.Contrib.Configuration --version 0.6.0-preview.7
  1. 新建类AppConfigConnectionStrings,用于存储数据库配置
/// <summary>
/// 应用配置类
/// </summary>
public class AppConfig : LocalMasaConfigurationOptions
{
public ConnectionStrings ConnectionStrings { get; set; }
} public class ConnectionStrings
{
public string DefaultConnection { get; set; }
}
  1. 修改文件appsettings.json
{
"AppConfig": {
"ConnectionStrings": {
"DefaultConnection": "server=localhost;uid=sa;pwd=P@ssw0rd;database=identity"
}
}
}
  1. 注册MasaConfiguration,修改类Program
builder.AddMasaConfiguration();
  1. 如果使用?修改类Program
app.MapGet("/AppConfig", (IOptions<AppConfig> appConfig)
{
return appConfig.Value.ConnectionStrings.DefaultConnection);
});

如果希望监听配置变更事件,则可使用IOptionsMonitor的OnChange方法

远程配置

目前我们远程配置的能力仅实现了Dcc, 下面就让我们看看如何来使用它

  1. 选中Assignment.MasaConfiguration,并安装Masa.Contrib.Configuration.ConfigurationApi.Dcc
dotnet add package Masa.Contrib.Configuration.ConfigurationApi.Dcc --version 0.6.0-preview.7
  1. 修改appsettings.json
{
//Dcc配置,扩展Configuration能力,支持远程配置
"DccOptions": {
"ManageServiceAddress ": "http://localhost:8890",
"RedisOptions": {
"Servers": [
{
"Host": "localhost",
"Port": 8889
}
],
"DefaultDatabase": 0,
"Password": ""
}
}
}
  1. 新建类RedisOptions, 用于配置业务项目中使用的缓存地址
public class RedisOptions : ConfigurationApiMasaConfigurationOptions
{
public string Host { get; set; } public int Port { get; set; } public string Password { get; set; } public int DefaultDatabase { get; set; }
}
  1. 修改类Program
var app = builder.AddMasaConfiguration(configurationBuilder =>
{
configurationBuilder.UseDcc();
}).Build();
  1. 如何使用?
// 推荐使用,通过IOptions<TOptions>获取配置,支持强类型
app.MapGet("/AppConfig", (IOptions<RedisOptions> options)
{
return options.Value.Host;
});

进阶

到目前为止,我们已经学会了如何使用Masa提供的配置,但只有了解原理,我们才敢在项目中大胆的用起来,出现问题后才能快速的定位并解决问题,下面我们就来深入了解下

分类

根据使用场景我们将配置划分为:

  • 本地配置(配置存储在本地配置文件中,后期配置变更不变)
  • 远程配置(配置在远程配置中心、例如Dcc、Apollo、其它配置中心)

IConfiguration结构

在使用MasaConfiguration后,IConfiguration的文件结构变更为:

IConfiguration
├── Local 本地节点(固定)
│ ├── Platforms 自定义配置
│ ├── ├── Name 参数
├── ConfigurationAPI 远程节点(固定)
│ ├── AppId 替换为你的AppId
│ ├── AppId ├── Platforms 自定义节点
│ ├── AppId ├── Platforms ├── Name 参数

除了一下配置源以及配置的提供者提供的配置除外,其余的配置会迁移到Local节点下

全局配置

MasaConfiguration中提供了全局配置的功能,并默认支持AppIdEnvironmentCluster

  1. 优先级

获取参数值的优先级为:

自定义全局配置 > 从IConfiguration中获取(支持命令、环境变量、配置文件) > 约定配置
  1. 自定义全局配置
service.Configure<MasaAppConfigureOptions>(options =>
{
options.AppId = "Replace-With-Your-AppId";
options.Environment = "Replace-With-Your-Environment";
options.Cluster = "Replace-With-Your-Cluster"; options.TryAdd("Replace-With-Your-ConfigKey", "Replace-With-Your-ConfigValue");// 自定义全局配置键、值
})
  1. IConfiguration中获取

当未指定配置的值时,将会从配置中获取得到配置的值,默认配置与Key的关系为:

  • AppId: AppId
  • Environment: ASPNETCORE_ENVIRONMENT
  • Cluster: Cluster

当命令行与环境变量获取参数失败后,则会尝试从配置文件根据配置的Key获取对应的值

  1. 约定默认值

当未自定义配置,且无法从IConfiguration中获取到相对应参数的配置后,我们将根据约定好的规则生成对应的值

  • AppId: 启动程序名.Replace(".", "-")
  • Environment: Production
  • Cluster: Default

配置映射

在快速入门的例子中,看似很简单就可以通过IOptions<TOptions>获取到AppConfig的配置信息以及Dcc中配置的Redis信息,这一切是如何做到的呢?

在MasaConfiguration中提供了两种映射方式,用来映射配置与类的对应关系,分别是:自动映射、手动映射。

  1. 自动映射

分为本地配置以及远程配置的自动映射

  • 本地配置: 由Masa.Contrib.Configuration提供
  • 远程配置
    • Dcc: 由Masa.Contrib.Configuration.ConfigurationApi.Dcc提供

1.1 当配置存储在本地时,则将对应的配置类继承LocalMasaConfigurationOptions

// <summary>
/// 应用配置类
/// </summary>
public class AppConfig : LocalMasaConfigurationOptions
{
// /// <summary>
// /// 如果当前配置挂载在根节点(一级节点)时,则无需重载,如果挂载在二级节点时,则需要重载ParentSection并赋值为一级节点名
// /// 根节点名:默认为一级节点,可不写,格式:一级节点:二级节点:三级节点……
// /// </summary>
// [JsonIgnore]
// public override string? ParentSection => null; // /// <summary>
// /// 如果类名与节点名保持一致,则可忽略不写,否则重写`Section`并赋值为节点名
// /// </summary>
// [JsonIgnore]
// public override string? Section => "RabbitMq"; public ConnectionStrings ConnectionStrings { get; set; }
} public class ConnectionStrings
{
public string DefaultConnection { get; set; }
}

当配置中的参数直接平铺挂载根节点下,而不是挂载到跟节点下的某个指定节点时,ParentSection无需重载,Section需要重载并赋值为空字符串

1.2 当配置存储在Dcc,则将对应的配置类继承ConfigurationApiMasaConfigurationOptions

public class RedisOptions : ConfigurationApiMasaConfigurationOptions
{
/// <summary>
/// 配置所属的AppId,当AppId与默认AppId一致时,可忽略
/// </summary>
// public virtual string AppId { get; } /// <summary>
/// Dcc的配置对象名称,当配置对象名称与类名一致时,可忽略
/// </summary>
// public virtual string? ObjectName { get; } public string Host { get; set; } public int Port { get; set; } public string Password { get; set; } public int DefaultDatabase { get; set; }
}
  1. 手动映射

虽然自动映射的方式很简单,也很方便,但总是有一些场景使得我们无法通过自动映射来做,那如何手动指定映射关系呢?

为了方便大家理解,手动映射仍然使用AppConfig以及Redis来举例

builder.AddMasaConfiguration(configurationBuilder =>
{
configurationBuilder.UseDcc();//使用Dcc 扩展Configuration能力,支持远程配置 configurationBuilder.UseMasaOptions(options =>
{ options.MappingLocal<AppConfig>("AppConfig");//其中参数"AppConfig"可不写(当类与节点名称一致时可忽略)
options.MappingConfigurationApi<RedisOptions>("{替换为Dcc中配置所属的AppId}", "{配置对象名称}");//其中配置对象名称可不写(当配置对象名与类名一致时可忽略)
});
});

Dcc配置

完整的Dcc配置如下:

{
"DccOptions": {
"ManageServiceAddress ": "http://localhost:8890",
"RedisOptions": {
"Servers": [
{
"Host": "localhost",
"Port": 8889
}
],
"DefaultDatabase": 0,
"Password": ""
},
"AppId": "Replace-With-Your-AppId",
"Environment": "Development",
"ConfigObjects": [ "Platforms" ],
"Secret": "",
"Cluster": "Default",
"ExpandSections" : [
{
"AppId": "Replace-With-Your-AppId",
"Environment": "Development",
"ConfigObjects": [ "Platforms" ],
"Secret": "",
"Cluster": "Default",
}
],
"PublicId": "Replace-With-Your-Public-AppId",
"PublicSecret": "Replace-With-Your-Public-AppId-Secret"
}
}
  • ManageServiceAddress: 用于更新远程配置使用,非必填
  • RedisOptions:Dcc会在Redis中存储配置的副本,此处是存储Dcc配置的的Redis地址(*)
  • AppId:项目中需要获取配置的AppId,也被称为Dcc的默认AppId,当未赋值时从全局配置中获取
  • Environment:项目中需要获取配置的环境信息,当未赋值时从全局配置中获取
  • ConfigObjects:项目中需要使用的配置对象名称,未赋值时默认获取当前环境、当前集群、当前AppId下的全部配置对象
  • Secret:秘钥,用于更新远程配置,每个AppId有一个秘钥,非必填(不可使用更新远程配置的能力)
  • Cluster:需要加载配置的集群,后面我们简称为Dcc的默认集群,未赋值时从全局配置中获取
  • PublicId:Dcc中公共配置的AppId,默认:public-$Config,非必填
  • PublicSecret:Dcc中公共配置的AppId的秘钥,非必填
  • ExpandSections:扩展配置的集合,适用于当前应用需要获取多个AppId下的配置时使用,其中AppId为必填项、Environment、Cluster为非必填项,当不存在时将与Dcc默认环境、集群一致,非必填

扩展其它的配置中心

上面提到了目前的远程配置能力仅支持Dcc,那如果我希望接入自己开发的配置中心或者其它更优秀的配置中心需要接入如何做?

Apollo为例:

  1. 新建类库Masa.Contrib.Configuration.ConfigurationApi.Apollo

  2. 新建ApolloConfigurationRepository并实现类AbstractConfigurationRepository

internal class ApolloConfigurationRepository : AbstractConfigurationRepository
{
private readonly IConfigurationApiClient _client;
public override SectionTypes SectionType => SectionTypes.ConfigurationAPI; public DccConfigurationRepository(
IConfigurationApiClient client,
ILoggerFactory loggerFactory)
: base(loggerFactory)
{
_client = client; //todo: 借助 IConfigurationApiClient 获取需要挂载到远程节点的配置信息并监听配置变化
// 当配置变更时触发FireRepositoryChange(SectionType, Load());
} public override Properties Load()
{
//todo: 返回当前挂载到远程节点的配置信息
}
}
  1. 新建类ConfigurationApiClient,为ConfigurationApi提供获取基础配置的能力
public class ConfigurationApiClient : IConfigurationApiClient
{
public Task<(string Raw, ConfigurationTypes ConfigurationType)> GetRawAsync(string configObject, Action<string>? valueChanged = null)
{
throw new NotImplementedException();
} public Task<(string Raw, ConfigurationTypes ConfigurationType)> GetRawAsync(string environment, string cluster, string appId, string configObject, Action<string>? valueChanged = null)
{
throw new NotImplementedException();
} public Task<T> GetAsync<T>(string configObject, Action<T>? valueChanged = null);
{
throw new NotImplementedException();
}
public Task<T> GetAsync<T>(string environment, string cluster, string appId, string configObject, Action<T>? valueChanged = null);
{
throw new NotImplementedException();
}
public Task<dynamic> GetDynamicAsync(string environment, string cluster, string appId, string configObject, Action<dynamic> valueChanged)
{
throw new NotImplementedException();
} public Task<dynamic> GetDynamicAsync(string key)
{
throw new NotImplementedException();
}
}
  1. 新建类ConfigurationApiManage,为ConfigurationApi提供管理配置的能力
public class ConfigurationApiManage : IConfigurationApiManage
{ // 通过管理端初始化AppId下的远程配置
public Task InitializeAsync(string environment, string cluster, string appId, Dictionary<string, string> configObjects)
{
throw new NotImplementedException();
} // 通过管理端更新指定配置的信息
public Task UpdateAsync(string environment, string cluster, string appId, string configObject, object value)
{
throw new NotImplementedException();
}
}
  1. 新建ConfigurationApiMasaConfigurationOptions类,并继承MasaConfigurationOptions

我们希望其它自定义配置也能根据约定实现自动映射,我们也清楚不同的配置中心中存储配置的名称是不一样的,例如在Apollo中配置对象名称叫做命名空间,因此为了方便开发人员可以使用起来更方便,我们建议不同的配置中心可以有自己专属的属性,比如ApolloNamespace,以此来降低开发人员的学习成本

public abstract class ConfigurationApiMasaConfigurationOptions : MasaConfigurationOptions
{
/// <summary>
/// The name of the parent section, if it is empty, it will be mounted under SectionType, otherwise it will be mounted to the specified section under SectionType
/// </summary>
[JsonIgnore]
public sealed override string? ParentSection => AppId; //
public virtual string AppId => StaticConfig.AppId; /// <summary>
/// The section null means same as the class name, else load from the specify section
/// </summary>
[JsonIgnore]
public sealed override string? Section => Namespace; /// <summary>
///
/// </summary>
public virtual string? Namespace { get; } /// <summary>
/// Configuration object name
/// </summary>
[JsonIgnore]
public sealed override SectionTypes SectionType => SectionTypes.ConfigurationApi;
}
  1. 选中类库Masa.Contrib.BasicAbility.Apollo,并新建IMasaConfigurationBuilder的扩展方法UseApollo
public static class MasaConfigurationExtensions
{
public static IMasaConfigurationBuilder UseApollo(this IMasaConfigurationBuilder builder)
{
//todo:将IConfigurationApiClient、IConfigurationApiManage注册到到服务集合中,并通过builder.AddRepository()添加ApolloConfigurationRepository
return builder;
}
}

总结

  1. 如何使用MasaConfiguration?

    • 新增:builder.AddMasaConfiguration()
  2. 为何通过IOptions获取到的配置为空,但通过IConfiguration或者IMasaConfiguration根据节点可以获取到?

    • 检查下是否没有绑定节点关系,如何绑定节点关系请查看问题2
    • 检查节点绑定是否错误
  3. IConfigurationApiClientIConfiguration之间有什么关系?

    • IConfigurationApiClientIConfigurationApiManage分别是管理远程Api的客户端以及管理端,与IConfiguration相比,IConfigurationApiClient的信息更全,每次获取配置需要像配置中心请求获取数据,而IConfiguration是通过调用IConfigurationApiClient将需要使用的配置对象获取并添加到IConfiguration中,后续用户获取配置时无需向配置中心请求数据
  4. 远程配置对象更新后,IConfiguration中的信息会更新吗?为什么?

    • 会更新、远程配置更新后会通过valueChanged通知远程配置的提供者,然后远程配置的提供者会刷新本地的远程配置并通知IConfiguration重新刷新数据

Dcc: Distributed Configuration Center 是一个以DDD为指导思想、使用.Net6.0开发的分布式配置中心

本章源码

Assignment08

https://github.com/zhenlei520/MasaFramework.Practice

开源地址

MASA.Framework:https://github.com/masastack/MASA.Framework

MASA.EShop:https://github.com/masalabs/MASA.EShop

MASA.Blazor:https://github.com/BlazorComponent/MASA.Blazor

如果你对我们的 MASA Framework 感兴趣,无论是代码贡献、使用、提 Issue,欢迎联系我们

配置IConfiguration的更多相关文章

  1. 10分钟就能学会的.NET Core配置

    .NET Core为我们提供了一套用于配置的API,它为程序提供了运行时从文件.命令行参数.环境变量等读取配置的方法.配置都是键值对的形式,并且支持嵌套,.NET Core还内建了从配置反序列化为PO ...

  2. ASP.NET Core 运行原理解剖[2]:Hosting补充之配置介绍

    在上一章中,我们介绍了 ASP.NET Core 的启动过程,主要是对 WebHost 源码的探索.而本文则是对上文的一个补充,更加偏向于实战,详细的介绍一下我们在实际开发中需要对 Hosting 做 ...

  3. 【转】10分钟就能学会的.NET Core配置

    .NET Core为我们提供了一套用于配置的API,它为程序提供了运行时从文件.命令行参数.环境变量等读取配置的方法.配置都是键值对的形式,并且支持嵌套,.NET Core还内建了从配置反序列化为PO ...

  4. Net core学习系列(九)——Net Core配置

    一.简介 NET Core为我们提供了一套用于配置的API,它为程序提供了运行时从文件.命令行参数.环境变量等读取配置的方法.配置都是键值对的形式,并且支持嵌套,.NET Core还内建了从配置反序列 ...

  5. .net core的配置介绍(三):Options

    前两篇介绍的都是已IConfiguration为基础的配置,这里在说说.net core提供的一种全新的辅助配置机制:Options. Options,翻译成中文就是选项,可选择的意思,它依赖于.ne ...

  6. .net 温故知新:【8】.NET 中的配置从xml转向json

    一.配置概述 在.net framework平台中我们常见的也是最熟悉的就是.config文件作为配置,控制台桌面程序是App.config,Web就是web.config,里面的配置格式为xml格式 ...

  7. asp.net core 系列 17 通用主机 IHostBuilder

    一.概述 ASP.NET Core 通用主机 (HostBuilder),该主机对于托管不处理 HTTP 请求的应用非常有用.通用主机的目标是将 HTTP 管道从 Web 主机 API 中分离出来,从 ...

  8. asp.net core 系列 12 选项 TOptions

    一.概述 本章讲的选项模式是对Configuration配置的功能扩展. 讲这篇时有个专用名词叫“选项类(TOptions)” .该选项类作用是指:把选项类中的属性与配置来源中的键关联起来.举个例,假 ...

  9. .NET Core2.1获取自定义配置文件信息

    前言 .net core来势已不可阻挡.既然挡不了,那我们就顺应它.了解它并学习它.今天我们就来看看和之前.net版本的配置文件读取方式有何异同,这里不在赘述.NET Core 基础知识. ps:更新 ...

随机推荐

  1. 设置C#启动进程但不显示命令行窗口

    设置一下Process类型相关的配置属性即可,直接上代码. //记得引入命名空间 //using System.Diagnostics; //获得当前环境的基路径 string basePath = ...

  2. 在生产中部署ML前需要了解的事

    在生产中部署ML前需要了解的事 译自:What You Should Know before Deploying ML in Production MLOps的必要性 MLOps之所以重要,有几个原因 ...

  3. VueX的热更替你知道多少?

    前言 我们在使用Vuex的时候,会时不时的更改Vuex内的数据,但是页面不会随之更新,如果数据量大,一个数据依赖另一个数据的话,这样我们要是再刷新页面的话会把以前依赖的数据清空,效率特别低.所以,今天 ...

  4. skywalking链路监控

    1. 下载安装包官网地址:http://skywalking.apache.org/downloads/ 2. tar xzf apache-skywalking-apm-6.5.0.tar.gz - ...

  5. chrome请求cgi遇到net::ERR_INCOMPLETE_CHUNKED_ENCODING 200 (OK)

    测试一个web demo的时候,通过chrome请求板子上的web server的cgi时总是提示:net::ERR_INCOMPLETE_CHUNKED_ENCODING 200 (OK) 搜遍整个 ...

  6. VS无线振弦采集仪的常见问题

    1 无法开机( 1)检查电源连接是否正确,电压范围应为 DC10~24V,输出能力不低于 2A, 正负极连接正确.若电池极性接反,即便未进行过开机操作也会导致设备永久性损坏.( 2)若使用电池供电,则 ...

  7. (一)java基础篇---第一个程序

    先认识java的基础知识 1.变量命名规则 :1)变量名由数字字母下划线组成,2)不能使用java的关键字,比如public这种,3)遵循小驼峰命名法 2.数据类型 2.1基本数据类型有8种 其中分为 ...

  8. vite搭建一个vue2的框架

    01-创建一个基础的模板框架 npm init vite@latest  02-安装依赖 npm install npm install vue@2.x vue-template-compiler@2 ...

  9. B.E.M 规范

    BEM文档 BEM: A New Front-End Methodology 如何看待 CSS 中 BEM 的命名方式? Battling BEM CSS: 10 Common Problems An ...

  10. 2022-7-9 第五小组 潘堂智 html学习笔记

    什么是 HTML? HTML 是用来描述网页的一种语言. HTML 指的是超文本标记语言 (Hyper Text Markup Language) HTML 不是一种编程语言,而是一种标记语言 (ma ...