前言

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

我们希望新的配置:

  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. SAP 上传 函数 ALSM_EXCEL_TO_INTERNAL_TABLE 不稳定

    ALSM_EXCEL_TO_INTERNAL_TABLE 这个函数的使用率非常高,然而有时候,它非常的不稳定,有时没有填充excel数据. 1.建议用户在开始运行界面程序之前先关闭Outlook. 2 ...

  2. 再测云原生数据库性能:PolarDB依旧最强,TDSQL-C、GaussDB变化不大

    1.摘要 近期,腾讯云数据库在文章「腾讯云TDSQL-C重磅升级,性能全面领跑云原生数据库市场」中提到,某些场景下性能有非常大的提升,且超过国内某橙色云厂商.恰好,在5月份,我们对各个厂商的云原生数据 ...

  3. Java获取当天或者明天等零点时间(00:00:00)0时0分0秒的方法

    SimpleDateFormat sdfYMD = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); Calendar calendar = ...

  4. docker的平替--podman

    前言 我们都知道,docker这个东西,是CaaS(Container as a Service,容器即服务)的通常解法.我们使用docker来管理容器的生命周期,比如镜像的生成.容器的管理和定制(D ...

  5. 集合-Collection工具类

    一.概念 二.常用方法 1.Collection和Collections的区别 Collection:是创建集合的接口,Collections是一个操作Collection工具类 2.常用方法 点击查 ...

  6. day03_2_流程控制

    # 流程控制 学习目标: ~~~txt1. idea安装与使用2. 流程控制if...else结构3. 流程控制switch结构4. 流程控制循环结构5. 流程控制关键字~~~ # 一.流程控制概述 ...

  7. 简单易用的任务队列-beanstalkd

    概述 beanstalkd 是一个简单快速的分布式工作队列系统,协议基于 ASCII 编码运行在 TCP 上.其最初设计的目的是通过后台异步执行耗时任务的方式降低高容量 Web 应用的页面延时.其具有 ...

  8. WPF 实现用户头像选择器

    制作一个用户头像选择器仿 WeGame 制作一个用户头像选择Canvas为父控件所实现,展示图片使用Image,Path当作上方的蒙版; Canvas:主要用途方便移动Image,设置ClipToBo ...

  9. C#异步延迟Task.Delay

    一. 1.Task.Delay实质是创建一个任务,再任务中开启一个定时间,然后延时指定的时间2.Task.Delay不和await一起使用情况,当代码遇到Task.Delay一句时,创建了了一个新的任 ...

  10. Jetpack Compose学习(8)——State及remeber

    原文地址: Jetpack Compose学习(8)--State状态及remeber关键字 - Stars-One的杂货小窝 之前我们使用TextField,使用到了两个关键字remember和mu ...