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 年发布安装速度较慢(旧版本),但自 npm 5 后有所改善较快,尤其是在缓存方面极快,使用硬链接和全局缓存来提高速度包管理模式扁平化依赖,可能会发生重复依…...
正点原子repo放到自己的git服务器
atk-rk3568_android11 导出project-objects对应仓库 .repo/repo/repo list -n > project-object.txt将project-object.txt格式化,并通过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如何选择定长或变长字符串? 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,找到Python Interpreter,再点击Add 3、选择第一个local interpreter 4、如图: 5、找到anaconda安装位置中的envs文件夹,在里面选择需要添加的python环境,如…...

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

人工智能之深度学习_[2]-PyTorch入门
PyTorch 1.PyTorch简介 1.1 什么是PyTorch PyTorch是一个基于Python的科学计算包 PyTorch安装 pip install torch -i https://pypi.tuna.tsinghua.edu.cn/simplePyTorch一个基于Python语言的深度学习框架,它将数据封装成张量(Tensor)来进行…...

基于Java的语音陪聊软件——支持聊天私聊-礼物系统-直播系统-缘分匹配-游戏陪玩
丰富的经验、成熟的技术,打造适合当下市场发展的语音交友软件源码。Java 语言凭借其独特的优势,为这款语音陪聊软件的稳健运行和持续发展奠定了坚实基础。它不仅融合了聊天私聊、礼物系统和直播系统等实用且有趣的功能,还创新性地引入了缘分匹…...
Go语言的文件操作
Go语言的文件操作 Go语言是一种开源的编程语言,由谷歌开发,具有简单、高效和并发的特点。在日常开发中,文件操作是一个非常重要且常见的任务。从读取配置文件到写入日志文件,从处理数据到存储结果,文件操作无处不在。…...
php审计1-extract函数变量覆盖
php审计1-extract函数变量覆盖 这是一个关于php审计的栏目,本人也是初学者,分享一下网上的关于php审计的一些知识,学习一下php的语法,顺便记录一下学习过程。 以下是一个关于php审计ctf题 <?php$flagflag.txt; extract($_…...

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

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

Elasticsearch:Jira 连接器教程第一部分
作者:来自 Elastic Gustavo Llermaly 将我们的 Jira 内容索引到 Elaasticsearch 中以创建统一的数据源并使用文档级别安全性进行搜索。 在本文中,我们将回顾 Elastic Jira 原生连接器的一个用例。我们将使用一个模拟项目,其中一家银行正在开发…...
ThreeJs功能演示——几何体操作导入导出
1、内部创建几何体导出编辑能力 1)支持内部创建的面、正方体、球体 内部创建物体时,如果是三维物体,要创建集合形状geometry,和对应的材质material。再一起创建一个三维物体。 // 存储创建的几何体列表const geometries [];cre…...
LeetCode::2270. 分割数组的方案数
2270. 分割数组的方案数 思路 前缀和 提示 给你一个下标从 0 开始长度为 n 的整数数组 nums 。 如果以下描述为真,那么 nums 在下标 i 处有一个 合法的分割 : 前 i 1 个元素的和 大于等于 剩下的 n - i - 1 个元素的和。下标 i 的右边 至少有一个 元…...

elementui表单验证,数据层级过深验证失效
先看示例代码,代码为模拟动态获取表单数据,然后动态添加rules验证规则,示例表单内输入框绑定form内第四层: <template><el-form :model"form" :rules"rules" ref"ruleForm" label-width&…...
【Java】LinkedHashMap (LRU)淘汰缓存的使用
文章目录 **1. initialCapacity(初始容量)****2. loadFactor(加载因子)****3. accessOrder(访问顺序)****完整参数解释示例****示例验证** LinkedHashMap 在 Java 中可维护元素插入或访问顺序,并…...
CancerGPT :基于大语言模型的罕见癌症药物对协同作用少样本预测研究
今天我们一起来剖析一篇发表于《npj Digital Medicine》的论文——《CancerGPT for few shot drug pair synergy prediction using large pretrained language models》。该研究聚焦于一个极具挑战性的前沿领域:如何利用大语言模型(LLMs)在数…...

《汽车维护与修理》是什么级别的期刊?是正规期刊吗?能评职称吗?
问题解答: 问:《汽车维护与修理》是不是核心期刊? 答:不是,是知网收录的正规学术期刊。 问:《汽车维护与修理》级别? 答:国家级。主管单位:中国汽车维修行业协会 …...

DBAPI如何优雅的获取单条数据
API如何优雅的获取单条数据 案例一 对于查询类API,查询的是单条数据,比如根据主键ID查询用户信息,sql如下: select id, name, age from user where id #{id}API默认返回的数据格式是多条的,如下: {&qu…...
Android Bitmap治理全解析:从加载优化到泄漏防控的全生命周期管理
引言 Bitmap(位图)是Android应用内存占用的“头号杀手”。一张1080P(1920x1080)的图片以ARGB_8888格式加载时,内存占用高达8MB(192010804字节)。据统计,超过60%的应用OOM崩溃与Bitm…...

全志A40i android7.1 调试信息打印串口由uart0改为uart3
一,概述 1. 目的 将调试信息打印串口由uart0改为uart3。 2. 版本信息 Uboot版本:2014.07; Kernel版本:Linux-3.10; 二,Uboot 1. sys_config.fex改动 使能uart3(TX:PH00 RX:PH01),并让boo…...

云原生玩法三问:构建自定义开发环境
云原生玩法三问:构建自定义开发环境 引言 临时运维一个古董项目,无文档,无环境,无交接人,俗称三无。 运行设备的环境老,本地环境版本高,ssh不过去。正好最近对 腾讯出品的云原生 cnb 感兴趣&…...

佰力博科技与您探讨热释电测量的几种方法
热释电的测量主要涉及热释电系数的测定,这是表征热释电材料性能的重要参数。热释电系数的测量方法主要包括静态法、动态法和积分电荷法。其中,积分电荷法最为常用,其原理是通过测量在电容器上积累的热释电电荷,从而确定热释电系数…...
Bean 作用域有哪些?如何答出技术深度?
导语: Spring 面试绕不开 Bean 的作用域问题,这是面试官考察候选人对 Spring 框架理解深度的常见方式。本文将围绕“Spring 中的 Bean 作用域”展开,结合典型面试题及实战场景,帮你厘清重点,打破模板式回答,…...
git: early EOF
macOS报错: Initialized empty Git repository in /usr/local/Homebrew/Library/Taps/homebrew/homebrew-core/.git/ remote: Enumerating objects: 2691797, done. remote: Counting objects: 100% (1760/1760), done. remote: Compressing objects: 100% (636/636…...
深度剖析 DeepSeek 开源模型部署与应用:策略、权衡与未来走向
在人工智能技术呈指数级发展的当下,大模型已然成为推动各行业变革的核心驱动力。DeepSeek 开源模型以其卓越的性能和灵活的开源特性,吸引了众多企业与开发者的目光。如何高效且合理地部署与运用 DeepSeek 模型,成为释放其巨大潜力的关键所在&…...

如何做好一份技术文档?从规划到实践的完整指南
如何做好一份技术文档?从规划到实践的完整指南 🌟 嗨,我是IRpickstars! 🌌 总有一行代码,能点亮万千星辰。 🔍 在技术的宇宙中,我愿做永不停歇的探索者。 ✨ 用代码丈量世界&…...
C#最佳实践:为何优先使用as或is而非强制转换
C#最佳实践:为何优先使用as或is而非强制转换 在 C# 的编程世界里,类型转换是我们经常会遇到的操作。就像在现实生活中,我们可能需要把不同形状的物品重新整理归类一样,在代码里,我们也常常需要将一个数据类型转换为另…...