前言

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

我们希望新的配置:

  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. SQL Server导出MDF数据库文件

    更新日志 2022年6月13日 发布. 2022年6月2日 开始. 一句话总结:先分离,然后复制. 先分离要导出mdf数据库文件的数据库. 在Microsoft SQL Server Manageme ...

  2. vue 使用npm install安装依赖失败 【问题分析与解决】

    1 进入项目根目录,先通过 npm install 命令安装项目所需依赖,再通过 vue ui 命令打开 Vue Cli 提供的图形化界面,选择项目所在文件夹将项目导入. 出现问题 npm insta ...

  3. 一、shell编程与变量

    目录 命令是什么 命令是如何运行的: 基本语法 解释器 注释 如何执行 输入.输出流 重定向 管道符 | 变量 常见Shell变量的类型包括: 变量命名原则 单引号和双引号 反引号 变量作用范围 查看 ...

  4. 快速全面了解QT软件界面开发技术

    快速全面了解QT软件界面开发技术     目录 前言 一. 学习QT可能的目的是什么? 只想体验一下QT? 当前的项目选择了用QT. 为将来做QT技术储备. 二. QT的核心技术优势是什么? QT在软 ...

  5. 基于SqlSugar的开发框架循序渐进介绍(9)-- 结合Winform控件实现字段的权限控制

    字段的权限控制,一般就是控制对应角色人员对某个业务对象的一些敏感字段的可访问性:包括可见.可编辑性等处理.本篇随笔结合基于SqlSugar的开发框架进行的字段控制管理介绍. 在设计字段权限的时候,我们 ...

  6. 使用React.js写一个类似单选框与复选框的功能

    单选框 <!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <tit ...

  7. 关于vue cli 使用iview 自定义主题遇到的坑

    定制主题,这里讲变量覆盖 当你老老实实的把上面文档中的代码一一复制粘贴到项目文件中时,发现了还没装less,所以你就 npm install less –savenpm install less-lo ...

  8. SAP 实例 4 CFW

    *&---------------------------------------------------------------------* *& Report demo_cfw ...

  9. dubbox、zookeeper BUG记录

    主要错误信息: dubbo:com.alibaba.dubbo.rpc.RpcException: Failed to invoke the method... Caused by: com.alib ...

  10. 02 CSS块级元素和行内元素

    02 CSS块级元素和行内元素 划分依据:根据标签内部可以存放的元素内容不同进行划分,它与CSS样式无关. 要先了解这个 得先了解 什么是容器级别的标签和文本级? 容器级标签 什么是容器级标签? 内部 ...