14:自定义配置数据源:低成本实现定制化配置方案-.NET Core开发实战
.NET Core开发实战前文传送门:
- 第1课:课程介绍
- 第2课:内容综述
- 第3课:.NET Core的现状、未来以及环境搭建
- 第4课:Startup:掌握ASP.NET Core的启动过程
- 第5课:依赖注入:良好架构的起点(上)
- 第5课:依赖注入:良好架构的起点(中)
- 第5课:依赖注入:良好架构的起点(下)
- 第6课:作用域与对象释放行为(上)
- 第6课:作用域与对象释放行为(下)
- 第7课:用Autofac增强容器能力(上)
- 第7课:用Autofac增强容器能力(下)
- 第8课:配置框架:让服务无缝适应各种环境
- 第9课:命令行配置提供程序
- 第10课:环境变量配置提供程序
- 第11课:文件配置提供程序
- 第12课:配置变更监听
- 第13课:配置绑定:使用强类型对象承载配置数据
14 | 自定义配置数据源:低成本实现定制化配置方案
这一节讲解如何定义自己的数据源,来扩展配置框架
扩展步骤
1、实现 IConfigurationSource
2、实现 IConfigurationProvider
3、实现 AddXXX 扩展方法,用来作为注入的快捷方式
源码链接:
https://github.com/witskeeper/geektime/tree/master/samples/ConfigurationCustom
首先定义一个 MyConfigurationSource
namespace ConfigurationCustom { class MyConfigurationSource : IConfigurationSource { public IConfigurationProvider Build(IConfigurationBuilder builder) { return new MyConfigurationProvider(); } } }
接着是 MyConfigurationProvider
namespace ConfigurationCustom { // ConfigurationProvider 集成自 IConfigurationProvider class MyConfigurationProvider : ConfigurationProvider { Timer timer; public MyConfigurationProvider() : base() { // 用一个线程模拟配置发生变化,每三秒钟执行一次,告诉我们要重新加载配置 timer = new Timer(); timer.Elapsed += Timer_Elapsed; timer.Interval = 3000; timer.Start(); } private void Timer_Elapsed(object sender, ElapsedEventArgs e) => Load(true); public override void Load() => Load(false); /// <summary> /// 加载数据 /// </summary> /// <param name="reload">是否重新加载数据</param> void Load(bool reload) { // Data 表示 Key-value 数据,这是由 ConfigurationProvider 提供的一个数据承载的集合 // 我们把最新的时间填充进去 Data["lastTime"] = DateTime.Now.ToString(); if (reload) { base.OnReload(); } } } }
实际上到此扩展就已经完成了,可以通过 builder.AddXXX 这个方法来把 source 注入进来
namespace ConfigurationCustom { class Program { static void Main(string[] args) { var builder = new ConfigurationBuilder(); builder.Add(new MyConfigurationSource()); var configRoot = builder.Build(); Console.WriteLine($"lastTime:{configRoot["lastTime"]}"); Console.ReadKey(); } } }
启动程序,输出如下:
lastTime:2020/3/1 22:39:36
这里可以看到,输出最新的时间
但是如果这样去分发配置源的包的话,需要把 MyConfigurationSource
定义为 public,否则使用方式没办法引用到这个类
那么就可以通过扩展方法的方式来保障不需要暴露 ConfigSource
定义一个扩展方法 AddMyConfiguration
namespace Microsoft.Extensions.Configuration { public static class MyConfigurationBuilderExtensions { public static IConfigurationBuilder AddMyConfiguration(this IConfigurationBuilder builder) { builder.Add(new MyConfigurationSource()); return builder; } } }
首先把扩展方法的命名空间放在 config 的命名空间,而不是自己的命名空间,这样方便在引用的时候直接使用而无需加载具体的命名空间
另外一个可以把 Provider 定义为 internal 的,默认是 internal,如果说分发到第三方的话,internal 的类是不能被引用的,这样就意味着只需要暴露一个扩展方法,而不需要暴露具体的配置源的实现
class MyConfigurationProvider : ConfigurationProvider
如何使用呢,其实很简单
只需要在 builder.Add 的时候使用 builder.AddMyConfiguration 就可以了,这样达到的效果是一样的
namespace ConfigurationCustom { class Program { static void Main(string[] args) { var builder = new ConfigurationBuilder(); //builder.Add(new MyConfigurationSource()); builder.AddMyConfiguration(); var configRoot = builder.Build(); Console.WriteLine($"lastTime:{configRoot["lastTime"]}"); Console.ReadKey(); } } }
启动程序,输出如下:
lastTime:2020/3/1 22:55:11
在定义扩展的时候,都推荐这样去做,把具体实现都定义为私有的,然后通过扩展方法的方式暴露出去
刚才实际上还定义了一个 timer 来模拟配置的变更,这里可以监听一下它的变更,看是否生效
上一节讲到 ChangeToken 的方式,这里还是用 ChangeToken 的 OnChange 方法
namespace ConfigurationCustom { class Program { static void Main(string[] args) { var builder = new ConfigurationBuilder(); builder.AddMyConfiguration(); var configRoot = builder.Build(); ChangeToken.OnChange(() => configRoot.GetReloadToken(), () => { Console.WriteLine($"lastTime:{configRoot["lastTime"]}"); }); Console.WriteLine("开始了"); Console.ReadKey(); } } }
启动程序,输出如下:
开始了 lastTime:2020/3/1 22:59:25 lastTime:2020/3/1 22:59:28 lastTime:2020/3/1 22:59:31
每个三秒钟输出一次,这说明我们定义的配置变更的通知已经生效了
MyConfigurationProvider 中我们只是通过赋值一个 DateTime 来模拟配置源
实际上可以从远程来说,比如阿波罗的配置中心,Kazoo,这些地方远程的读取配置,结合着命令行和环境变量配置,就可以完成配置中心的远程方案,意味着可以版本化的管理配置
这样子在 Docker 容器环境下面,Kubernetes 环境下面,就可以有完善的配置管理解决方案

原文出处:微信公众号【DotNet NB】,作者【郑子铭】
原文链接:https://mp.weixin.qq.com/s/tv2dbqwKZ9OKIMnjTn_KBg
本文观点不代表Dotnet9立场,转载请联系原作者。