当前位置: 首页 > news >正文

ASP.NET Core - 配置系统之自定义配置提供程序

ASP.NET Core - 配置系统之自定义配置提供程序

  • 4. 自定义配置提供程序
    • IConfigurationSource
    • IConfigurationProvider

4. 自定义配置提供程序

在 .NET Core 配置系统中封装一个配置提供程序关键在于提供相应的 IconfigurationSource 实现和 IConfigurationProvider 接口实现,这两个接口在上一章 ASP.NET Core - 配置系统之配置提供程序 中也有提到了。

IConfigurationSource

IConfigurationSource 负责创建 IConfigurationProvider 实现的实例。它的定义很简单,就一个Build方法,返回 IConfigurationProvider 实例:

public interface IConfigurationSource
{IConfigurationProvider Build(IConfigurationBuilder builder);
}

IConfigurationProvider

IConfigurationProvider 负责实现配置的设置、读取、重载等功能,并以键值对形式提供配置。

public interface IConfigurationProvider
{// 获取指定父路径下的直接子节点Key,然后 Concat(earlierKeys) 一同返回IEnumerable<string> GetChildKeys(IEnumerable<string> earlierKeys, string parentPath);// 当该配置提供程序支持更改追踪(change tracking)时,会返回 change token// 否则,返回 nullIChangeToken GetReloadToken();// 加载配置void Load();// 设置 key:valuevoid Set(string key, string value);// 尝试获取指定 key 的 valuebool TryGet(string key, out string value);
}

像工作中常用的配置中心客户端,例如 nacos、consul,都是实现了对应的配置提供程序,从而将配置中心中的配置无缝地接入到 .NET Core 的配置系统中进行使用,和本地配置文件的使用没有分别。

如果我们需要封装自己的配置提供程序,推荐直接继承抽象类 ConfigurationProvider,该类实现了 IConfigurationProvider 接口,继承自该类只要实现 Load 方法即可,Load 方法用于从配置来源加载解析配置信息,将最终的键值对配置信息存储到 Data 中。这个过程中可参考一下其他已有的配置提供程序的源码,模仿着去写自己的东西。

在我们日常的系统平台中,总少不了数据字典这样一个功能,用于维护平台中一些业务配置,因为是随业务动态扩展和变动的,很多时候不会写在配置文件,而是维护在数据库中。以下以这样一个场景实现一个配置提供程序。

因为是以数据库作为载体来存储配置信息,所以第一步就是定义实体类

public class DataDictioaryDO
{public int Id { get; set; }public int? ParentId { get; set; }public string Key { get; set; }public string Value { get; set; }
}

数据字典支持多级级联,通过 ParentId 关联上一级,ParentId 为空的即为根节点,如存在下级节点则 Value 值可以为空,就算填写了也无效,最终呈现出来的就是一个树结构。

然后就是定义相应的数据库访问上下文 DataDictionaryDbContext

public class DataDictionaryDbContext : DbContext
{public DbSet<DataDictioaryDO> DataDictioaries { get; set; }public DataDictionaryDbContext(DbContextOptions<DataDictionaryDbContext> options) : base(options){}protected override void OnModelCreating(ModelBuilder modelBuilder){base.OnModelCreating(modelBuilder);modelBuilder.Entity<DataDictioaryDO>().HasKey(e => e.Id);modelBuilder.Entity<DataDictioaryDO>().Property(e => e.Value).IsRequired(false);}
}

通过 DbContextOptions 交由外部去配置具体的数据库类型和连接字符串。

之后创建 IConfigurationSource 实现类,主要就是构造函数中需要传入数据库配置委托,并且在 Build 实例化EFDataDictionaryConfigurationProvider 对象。

public class EFDataDictionaryConfigurationSource : IConfigurationSource
{private readonly Action<DbContextOptionsBuilder> _action;public EFDataDictionaryConfigurationSource(Action<DbContextOptionsBuilder> action){_action= action;}public IConfigurationProvider Build(IConfigurationBuilder builder){return new EFDataDictionaryConfigurationProvider(_action);}
}

之后通过继承 ConfigurationProvider 实现 EFDataDictionaryConfigurationProvider,主要逻辑就是从数据库获取对应的数据表,如果表中没有数据则插入默认数据,再通过相应的解析器解析数据表数据生成一个 Dictionary<string, string> 对象。

public class EFDataDictionaryConfigurationProvider : ConfigurationProvider
{Action<DbContextOptionsBuilder> OptionsAction { get; }public EFDataDictionaryConfigurationProvider(Action<DbContextOptionsBuilder> action){OptionsAction = action;}public override void Load(){var builder = new DbContextOptionsBuilder<DataDictionaryDbContext>();OptionsAction(builder);using var dbContext = new DataDictionaryDbContext(builder.Options);if(dbContext == null){throw new Exception("Null DB Context !");}dbContext.Database.EnsureCreated();if (!dbContext.DataDictioaries.Any()){CreateAndSaveDefaultValues(dbContext);}Data = EFDataDictionaryParser.Parse(dbContext.DataDictioaries);}private void CreateAndSaveDefaultValues(DataDictionaryDbContext context){var datas = new List<DataDictioaryDO>{new DataDictioaryDO{Id = 1,Key = "Settings",},new DataDictioaryDO{Id = 2,ParentId = 1,Key = "Provider",Value = nameof(EFDataDictionaryConfigurationProvider)},new DataDictioaryDO{ Id = 3,ParentId = 1,Key = "Version",Value = "v1.0.0"}};context.DataDictioaries.AddRange(datas);context.SaveChanges();}
}

其中,解析器 EFDataDictionaryParser 的代码如下,主要就是通过递归的方式,通过树形数据的 key 构建完整的 key,并将其存入 Dictionary<string,string> 对象中。

internal class EFDataDictionaryParser
{private readonly IDictionary<string, string> _data = new SortedDictionary<string, string>(StringComparer.OrdinalIgnoreCase);private readonly Stack<string> _context = new();private string _currentPath;private EFDataDictionaryParser() { }public static IDictionary<string, string> Parse(IEnumerable<DataDictioaryDO> datas) =>new EFDataDictionaryParser().ParseDataDictionaryConfiguration(datas);private IDictionary<string, string> ParseDataDictionaryConfiguration(IEnumerable<DataDictioaryDO> datas){_data.Clear();if(datas?.Any() != true){return _data;}var roots = datas.Where(d => !d.ParentId.HasValue);foreach (var root in roots){EnterContext(root.Key);VisitElement(datas, root);ExitContext();}return _data;}private void VisitElement(IEnumerable<DataDictioaryDO> datas, DataDictioaryDO parent){var children = datas.Where(d => d.ParentId == parent.Id);if (children.Any()){foreach (var section in children){EnterContext(section.Key);VisitElement(datas, section);ExitContext();}}else{var key = _currentPath;if (_data.ContainsKey(key))throw new FormatException($"A duplicate key '{key}' was found.");_data[key] = parent.Value;}}private void EnterContext(string context){_context.Push(context);_currentPath = ConfigurationPath.Combine(_context.Reverse());}private void ExitContext(){_context.Pop();_currentPath = ConfigurationPath.Combine(_context.Reverse());}
}

之后为这个配置提供程序提供一个扩展方法,方便之后的使用,如下:

public static class EFDataDictionaryConfigurationExtensions
{public static IConfigurationBuilder AddEFDataDictionaryConfiguration(this IConfigurationBuilder builder, Action<DbContextOptionsBuilder> optionAction){builder.Add(new EFDataDictionaryConfigurationSource(optionAction));return builder;}
}

之后在入口文件中将我们的配置扩展程序添加到配置系统中,并指定使用内存数据库进行测试

using ConfigurationSampleConsole.ConfigProvider;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;using var host = Host.CreateDefaultBuilder(args).ConfigureAppConfiguration((context, config) =>{// 清除原有的配置提供程序config.Sources.Clear();config.AddEFDataDictionaryConfiguration(builder =>{builder.UseInMemoryDatabase("DataDictionary");});}).Build();var configuration = host.Services.GetService<IConfiguration>();Console.WriteLine($"Settings:Provider: {configuration.GetValue<string>("Settings:Provider")}");
Console.WriteLine($"Settings:Version: {configuration.GetValue<string>("Settings:version")}");host.Run();

最后的控制台输出结果如下:

在这里插入图片描述
以上就是 .NET Core 框架下配置系统的一部分知识点,更加详尽的介绍大家可以再看看官方文档。配置系统很多时候是结合选项系统一起使用的,下一篇将介绍一下 .NET Core 框架下的选项系统。



参考文章:

ASP.NET Core 中的配置 | Microsoft Learn
配置 - .NET | Microsoft Learn
理解ASP.NET Core - 配置(Configuration)



ASP.NET Core 系列总结:

目录:ASP.NET Core 系列总结
上一篇:ASP.NET Core — 配置系统之配置提供程序
下一篇:ASP.NET Core — 选项系统之选项配置

相关文章:

ASP.NET Core - 配置系统之自定义配置提供程序

ASP.NET Core - 配置系统之自定义配置提供程序 4. 自定义配置提供程序IConfigurationSourceIConfigurationProvider 4. 自定义配置提供程序 在 .NET Core 配置系统中封装一个配置提供程序关键在于提供相应的 IconfigurationSource 实现和 IConfigurationProvider 接口实现&…...

npm、yarn、pnpm包安装器差异性对比

特性npmyarnpnpm发布年份2010 年发布2016 年发布2017 年发布安装速度较慢&#xff08;旧版本&#xff09;&#xff0c;但自 npm 5 后有所改善较快&#xff0c;尤其是在缓存方面极快&#xff0c;使用硬链接和全局缓存来提高速度包管理模式扁平化依赖&#xff0c;可能会发生重复依…...

正点原子repo放到自己的git服务器

atk-rk3568_android11 导出project-objects对应仓库 .repo/repo/repo list -n > project-object.txt将project-object.txt格式化&#xff0c;并通过gitolite.conf创建对应仓库 atk-rk3568_android11_repo atk-rk3568_android11/RKTools atk-rk3568_android11_repo atk-…...

[MySQL | 二、基本数据类型]

基本数据类型 一、数值类型举例表结构1. 整数类型zerofill属性 与 int(n) 中 n 的关系 2.bit类型3. 小数类型float类型decimal类型 二、字符串类型1. char2. varchar如何选择定长或变长字符串&#xff1f; 3. 日期时间类型(date datetime timestamp)4. enum枚举类型5. set多选类…...

工作记录小点

postman问题 postman 返回404 可以先看看这个模块是否集成到pom文件中 postman 返回200不调debug 请求参数Json有错误请求方式不对Get/Post debug报错连接失败 host文件没copy同事的 2.对应组件的不同服务白名单没添加导致的 host文件刷新 进入 C:\windows\system32\drivers\e…...

在PyCharm中使用Anaconda中的虚拟环境

1、在File菜单中找到Settings 2、Settings中搜索interpreter&#xff0c;找到Python Interpreter&#xff0c;再点击Add 3、选择第一个local interpreter 4、如图&#xff1a; 5、找到anaconda安装位置中的envs文件夹&#xff0c;在里面选择需要添加的python环境&#xff0c;如…...

ansible基础

ansible 1.概述 ansible是基于python语言开发&#xff0c;配置管理和部署应用的工具。批量的配置&#xff0c;部署&#xff0c;管理“上千台”主机 &#xff08;实际一次100台左右&#xff09;&#xff0c;ansible只要在一台主机上就可以对其他主机进行操作。 &#xff08;1&…...

人工智能之深度学习_[2]-PyTorch入门

PyTorch 1.PyTorch简介 1.1 什么是PyTorch PyTorch是一个基于Python的科学计算包 PyTorch安装 pip install torch -i https://pypi.tuna.tsinghua.edu.cn/simplePyTorch一个基于Python语言的深度学习框架&#xff0c;它将数据封装成张量&#xff08;Tensor&#xff09;来进行…...

基于Java的语音陪聊软件——支持聊天私聊-礼物系统-直播系统-缘分匹配-游戏陪玩

丰富的经验、成熟的技术&#xff0c;打造适合当下市场发展的语音交友软件源码。Java 语言凭借其独特的优势&#xff0c;为这款语音陪聊软件的稳健运行和持续发展奠定了坚实基础。它不仅融合了聊天私聊、礼物系统和直播系统等实用且有趣的功能&#xff0c;还创新性地引入了缘分匹…...

Go语言的文件操作

Go语言的文件操作 Go语言是一种开源的编程语言&#xff0c;由谷歌开发&#xff0c;具有简单、高效和并发的特点。在日常开发中&#xff0c;文件操作是一个非常重要且常见的任务。从读取配置文件到写入日志文件&#xff0c;从处理数据到存储结果&#xff0c;文件操作无处不在。…...

php审计1-extract函数变量覆盖

php审计1-extract函数变量覆盖 这是一个关于php审计的栏目&#xff0c;本人也是初学者&#xff0c;分享一下网上的关于php审计的一些知识&#xff0c;学习一下php的语法&#xff0c;顺便记录一下学习过程。 以下是一个关于php审计ctf题 <?php$flagflag.txt; extract($_…...

百度热力图数据原理,处理及论文应用7

目录 0、数据简介0、示例数据1、百度热力图数据日期如何选择1.1、其他实验数据的时间1.2、看日历1.3、看天气 2、百度热力图几天够研究&#xff1f;部分文章统计3、数据原理3.1.1 ** 这个比较重要&#xff0c;后面还会再次出现。核密度的值怎么理解&#xff1f;**3.1.2 Csv->…...

端口镜像和端口安全

✍作者&#xff1a;柒烨带你飞 &#x1f4aa;格言&#xff1a;生活的情况越艰难&#xff0c;我越感到自己更坚强&#xff1b;我这个人走得很慢&#xff0c;但我从不后退。 &#x1f4dc;系列专栏&#xff1a;网络安全从菜鸟到飞鸟的逆袭 目录 一&#xff0c;端口镜像二&#xf…...

Elasticsearch:Jira 连接器教程第一部分

作者&#xff1a;来自 Elastic Gustavo Llermaly 将我们的 Jira 内容索引到 Elaasticsearch 中以创建统一的数据源并使用文档级别安全性进行搜索。 在本文中&#xff0c;我们将回顾 Elastic Jira 原生连接器的一个用例。我们将使用一个模拟项目&#xff0c;其中一家银行正在开发…...

ThreeJs功能演示——几何体操作导入导出

1、内部创建几何体导出编辑能力 1&#xff09;支持内部创建的面、正方体、球体 内部创建物体时&#xff0c;如果是三维物体&#xff0c;要创建集合形状geometry&#xff0c;和对应的材质material。再一起创建一个三维物体。 // 存储创建的几何体列表const geometries [];cre…...

LeetCode::2270. 分割数组的方案数

2270. 分割数组的方案数 思路 前缀和 提示 给你一个下标从 0 开始长度为 n 的整数数组 nums 。 如果以下描述为真&#xff0c;那么 nums 在下标 i 处有一个 合法的分割 &#xff1a; 前 i 1 个元素的和 大于等于 剩下的 n - i - 1 个元素的和。下标 i 的右边 至少有一个 元…...

elementui表单验证,数据层级过深验证失效

先看示例代码&#xff0c;代码为模拟动态获取表单数据&#xff0c;然后动态添加rules验证规则&#xff0c;示例表单内输入框绑定form内第四层&#xff1a; <template><el-form :model"form" :rules"rules" ref"ruleForm" label-width&…...

【Java】LinkedHashMap (LRU)淘汰缓存的使用

文章目录 **1. initialCapacity&#xff08;初始容量&#xff09;****2. loadFactor&#xff08;加载因子&#xff09;****3. accessOrder&#xff08;访问顺序&#xff09;****完整参数解释示例****示例验证** LinkedHashMap 在 Java 中可维护元素插入或访问顺序&#xff0c;并…...

CancerGPT :基于大语言模型的罕见癌症药物对协同作用少样本预测研究

今天我们一起来剖析一篇发表于《npj Digital Medicine》的论文——《CancerGPT for few shot drug pair synergy prediction using large pretrained language models》。该研究聚焦于一个极具挑战性的前沿领域&#xff1a;如何利用大语言模型&#xff08;LLMs&#xff09;在数…...

《汽车维护与修理》是什么级别的期刊?是正规期刊吗?能评职称吗?

​问题解答&#xff1a; 问&#xff1a;《汽车维护与修理》是不是核心期刊&#xff1f; 答&#xff1a;不是&#xff0c;是知网收录的正规学术期刊。 问&#xff1a;《汽车维护与修理》级别&#xff1f; 答&#xff1a;国家级。主管单位&#xff1a;中国汽车维修行业协会 …...

XML Group端口详解

在XML数据映射过程中&#xff0c;经常需要对数据进行分组聚合操作。例如&#xff0c;当处理包含多个物料明细的XML文件时&#xff0c;可能需要将相同物料号的明细归为一组&#xff0c;或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码&#xff0c;增加了开…...

变量 varablie 声明- Rust 变量 let mut 声明与 C/C++ 变量声明对比分析

一、变量声明设计&#xff1a;let 与 mut 的哲学解析 Rust 采用 let 声明变量并通过 mut 显式标记可变性&#xff0c;这种设计体现了语言的核心哲学。以下是深度解析&#xff1a; 1.1 设计理念剖析 安全优先原则&#xff1a;默认不可变强制开发者明确声明意图 let x 5; …...

【kafka】Golang实现分布式Masscan任务调度系统

要求&#xff1a; 输出两个程序&#xff0c;一个命令行程序&#xff08;命令行参数用flag&#xff09;和一个服务端程序。 命令行程序支持通过命令行参数配置下发IP或IP段、端口、扫描带宽&#xff0c;然后将消息推送到kafka里面。 服务端程序&#xff1a; 从kafka消费者接收…...

Opencv中的addweighted函数

一.addweighted函数作用 addweighted&#xff08;&#xff09;是OpenCV库中用于图像处理的函数&#xff0c;主要功能是将两个输入图像&#xff08;尺寸和类型相同&#xff09;按照指定的权重进行加权叠加&#xff08;图像融合&#xff09;&#xff0c;并添加一个标量值&#x…...

剑指offer20_链表中环的入口节点

链表中环的入口节点 给定一个链表&#xff0c;若其中包含环&#xff0c;则输出环的入口节点。 若其中不包含环&#xff0c;则输出null。 数据范围 节点 val 值取值范围 [ 1 , 1000 ] [1,1000] [1,1000]。 节点 val 值各不相同。 链表长度 [ 0 , 500 ] [0,500] [0,500]。 …...

Python爬虫(二):爬虫完整流程

爬虫完整流程详解&#xff08;7大核心步骤实战技巧&#xff09; 一、爬虫完整工作流程 以下是爬虫开发的完整流程&#xff0c;我将结合具体技术点和实战经验展开说明&#xff1a; 1. 目标分析与前期准备 网站技术分析&#xff1a; 使用浏览器开发者工具&#xff08;F12&…...

SpringCloudGateway 自定义局部过滤器

场景&#xff1a; 将所有请求转化为同一路径请求&#xff08;方便穿网配置&#xff09;在请求头内标识原来路径&#xff0c;然后在将请求分发给不同服务 AllToOneGatewayFilterFactory import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; impor…...

Spring AI与Spring Modulith核心技术解析

Spring AI核心架构解析 Spring AI&#xff08;https://spring.io/projects/spring-ai&#xff09;作为Spring生态中的AI集成框架&#xff0c;其核心设计理念是通过模块化架构降低AI应用的开发复杂度。与Python生态中的LangChain/LlamaIndex等工具类似&#xff0c;但特别为多语…...

2023赣州旅游投资集团

单选题 1.“不登高山&#xff0c;不知天之高也&#xff1b;不临深溪&#xff0c;不知地之厚也。”这句话说明_____。 A、人的意识具有创造性 B、人的认识是独立于实践之外的 C、实践在认识过程中具有决定作用 D、人的一切知识都是从直接经验中获得的 参考答案: C 本题解…...

AI,如何重构理解、匹配与决策?

AI 时代&#xff0c;我们如何理解消费&#xff1f; 作者&#xff5c;王彬 封面&#xff5c;Unplash 人们通过信息理解世界。 曾几何时&#xff0c;PC 与移动互联网重塑了人们的购物路径&#xff1a;信息变得唾手可得&#xff0c;商品决策变得高度依赖内容。 但 AI 时代的来…...