网站后台系统功能,河南省建设厅八大员网站,广告推广图片,业务系统管理软件ASP.NET Core - 配置系统之自定义配置提供程序 4. 自定义配置提供程序IConfigurationSourceIConfigurationProvider 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) 一同返回IEnumerablestring GetChildKeys(IEnumerablestring 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 DbSetDataDictioaryDO DataDictioaries { get; set; }public DataDictionaryDbContext(DbContextOptionsDataDictionaryDbContext options) : base(options){}protected override void OnModelCreating(ModelBuilder modelBuilder){base.OnModelCreating(modelBuilder);modelBuilder.EntityDataDictioaryDO().HasKey(e e.Id);modelBuilder.EntityDataDictioaryDO().Property(e e.Value).IsRequired(false);}
}通过 DbContextOptions 交由外部去配置具体的数据库类型和连接字符串。
之后创建 IConfigurationSource 实现类主要就是构造函数中需要传入数据库配置委托并且在 Build 实例化EFDataDictionaryConfigurationProvider 对象。
public class EFDataDictionaryConfigurationSource : IConfigurationSource
{private readonly ActionDbContextOptionsBuilder _action;public EFDataDictionaryConfigurationSource(ActionDbContextOptionsBuilder action){_action action;}public IConfigurationProvider Build(IConfigurationBuilder builder){return new EFDataDictionaryConfigurationProvider(_action);}
}之后通过继承 ConfigurationProvider 实现 EFDataDictionaryConfigurationProvider主要逻辑就是从数据库获取对应的数据表如果表中没有数据则插入默认数据再通过相应的解析器解析数据表数据生成一个 Dictionarystring, string 对象。
public class EFDataDictionaryConfigurationProvider : ConfigurationProvider
{ActionDbContextOptionsBuilder OptionsAction { get; }public EFDataDictionaryConfigurationProvider(ActionDbContextOptionsBuilder action){OptionsAction action;}public override void Load(){var builder new DbContextOptionsBuilderDataDictionaryDbContext();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 ListDataDictioaryDO{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并将其存入 Dictionarystring,string 对象中。
internal class EFDataDictionaryParser
{private readonly IDictionarystring, string _data new SortedDictionarystring, string(StringComparer.OrdinalIgnoreCase);private readonly Stackstring _context new();private string _currentPath;private EFDataDictionaryParser() { }public static IDictionarystring, string Parse(IEnumerableDataDictioaryDO datas) new EFDataDictionaryParser().ParseDataDictionaryConfiguration(datas);private IDictionarystring, string ParseDataDictionaryConfiguration(IEnumerableDataDictioaryDO 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(IEnumerableDataDictioaryDO 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, ActionDbContextOptionsBuilder 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.GetServiceIConfiguration();Console.WriteLine($Settings:Provider: {configuration.GetValuestring(Settings:Provider)});
Console.WriteLine($Settings:Version: {configuration.GetValuestring(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 — 选项系统之选项配置