02020305 .NET Core核心基础组件05-开发自己的配置提供者
02020305 .NET Core核心基础组件05-开发自己的配置提供者
1. 开发自己的配置提供者(视频2-35)
1.1 开发自定义配置提供者的步骤
- step1 → 开发一个直接或者间接实现IConfigurationProvider接口的类XXXConfigurationProvider,一般继承自ConfigurationProvider。如果是从文件读取,可以继承自FileConfigurationProvider。重写Load方法,把“扁平化数据”设置到Data属性即可。
- step2 → 再开发一个实现了IConfigurationSource接口的类XXXConfigurationSource。如果是从文件读取,可以继承自FileConfigurationSource。在Build方法中返回上面的ConfigurationProvider对象。
- step3 → 然后使用即可,configurationBuilder.Add(new ConfigurationSource())即可。为了简化使用,一般提供一个IConfigurationBuilder的扩展方法。
- 整体流程简述:编写ConfigurationProvider类实际读取配置;编写ConfigurationSource在Build中返回ConfigurationProvider对象;把ConfigurationSource对象加入IConfigurationBuilder。
1.2 开发web.config提供者
- .NET Core中不建议使用.NET Framework里的web.config,不过仍然继续提供了ConfigurationManager了,不过没有官方支持通过新的Configuration框架读取的方式。我们来实现一个,能够读取web.config里的connectionStrings和appSettings的内容。没用过web.config也没关系。
- 这个项目主要意义还是展示如何编写自定义配置提供者,实用意义不大。主要为下一个更实用的配置提供者做知识准备。
1.3 web.config格式
// web.config
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<connectionStrings>
<add name="connstr1" connectionString="Data Source=.;Initial Catalog=DemoDB;User ID=sa;Password=123456" providerName="System.Data.SqlClient"/>
</connectionStrings>
<appSettings>
<add key="Smtp:Server" value="smtp.test.com" />
<add key="Smtp.Port" value="25" />
<add key="RedisServer" value="127.0.0.1" />
<add key="RedisPassword" value="abc123" />
</appSettings>
</configuration>
- configuration → 根节点
- connectionStrings → 配置的是连接字符串
- appSettings → 普通配置,用于非连接字符串
1.4 解析xml文件的类并映射为扁平化结果
- 不再讲解xml以及XmlDocument的基础知识。继承自FileConfigurationProvider
//Key是大小写不敏感的
var data = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(stream);
var nodeConnStrs = xmlDoc.SelectNodes("/configuration/connectionStrings/add");
foreach(var nodeConnStr in nodeConnStrs.Cast<XmlNode>())
{
string name = nodeConnStr.Attributes["name"].Value;
string connectionString = nodeConnStr.Attributes["connectionString"].Value;
data[$"{name}:connectionString"] = connectionString;
var providerNameProp = nodeConnStr.Attributes["providerName"];
if(providerNameProp!=null)//providerName is optional
{
string providerName = providerNameProp.Value;
data[$"{name}:providerName"] = providerName;
}
}
说明:
1. XmlDocument → 用来解析xml文件。自行在网上搜索XmlDocument的使用和xml相关知识。
2. Configuration框架约定大小写是不敏感的。OrdinalignoreCase:大小写不敏感。
2. 项目示例
2.1 项目目录
MyConfigProviderDemo → 项目名称,.NET Core 5.0控制台项目。
├── MyConfigProvider.cs
├── MyConfigSource.cs
├── TestWebConfig.cs
├── WebConfig.cs
├── MyConfigExtensions.cs
├── web.config
└── Program.cs
需要安装如下5个包:
microsoft.extensions.configuration
microsoft.extensions.configuration.binder
microsoft.extensions.configuration.json
microsoft.extensions.options
microsoft.extensions.dependencyinjection
2.2 源代码
// MyConfigExtensions.cs
using Microsoft.Extensions.Configuration;
namespace MyConfigProviderDemo
{
static class MyConfigExtensions
{
public static IConfigurationBuilder AddMyConfig(this IConfigurationBuilder cb, string path=null)
{
if(path == null)
{
path = "web.config";
}
cb.Add(new MyConfigSource() { Path = path });
return cb;
}
}
}
—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—
// MyConfigProvider.cs
using Microsoft.Extensions.Configuration;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Xml;
namespace MyConfigProviderDemo
{
class MyConfigProvider : FileConfigurationProvider
{
public MyConfigProvider(MyConfigSource src) : base(src) // 调用父类的构造函数
{
}
public override void Load(Stream stream) // 读取的配置文件流
{
var data = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); // StringComparer.OrdinalIgnoreCase表示忽略大小写
XmlDocument xmlDoc = new XmlDocument(); // 创建一个xml对象
xmlDoc.Load(stream); // 加载xml文件
/*
* web.config文档
<configuration>
<connectionStrings>
<add name="connstring" connectionString="abc" providerName="mysql"/>
<add name="conntext" connectionString="123" providerName="sqlserver"/>
</connectionStrings>
<appSettings>
<add key="AppStts:name" value="smtp.test.com" />
<add key="AppStts:age" value="25" />
<add key="AppStts:proxy.address" value="127.0.0.1" />
<add key="AppStts:proxy:ids:0" value="666" />
<add key="AppStts:proxy:ids:1" value="777" />
</appSettings>
</configuration>
*/
// 将connectionStrings中的节点扁平化映射为:[cstr:{connectionString:"abc",providerName:"mysql"}, ctext:{connectionString:"123",providerName:"sqlserver"}]
XmlNodeList connStringsNode = xmlDoc.SelectNodes("/configuration/connectionStrings/add"); // 拿到connectionStrings下所有(2个)节点:connstring和conntext
foreach (XmlNode xmlNode in connStringsNode.Cast<XmlNode>()) // Cast<XmlNode>()转换节点集合,遍历connStringsNode(即connstring和conntext集合)转换为XmlNode
{
string name = xmlNode.Attributes["name"].Value;
string connectionString = xmlNode.Attributes["connectionString"].Value;
data[$"{name}:connectionString"] = connectionString; // 解析xml文件,将结果扁平化映射到data中
var attProviderName = xmlNode.Attributes["providerName"];
if (attProviderName != null)
{
data[$"{name}:providerName"] = attProviderName.Value; // 解析xml文件,将结果扁平化映射到data中
}
}
// 将appSettings中的节点扁平化映射为:[key:value形式]
XmlNodeList appSettings = xmlDoc.SelectNodes("/configuration/appSettings/add"); // 拿到connectionStrings下所有(2个)节点:connstring和conntext
foreach (XmlNode xmlNode in appSettings.Cast<XmlNode>()) // Cast<XmlNode>()转换节点集合,遍历connStringsNode(即connstring和conntext集合)转换为XmlNode
{
string key = xmlNode.Attributes["key"].Value;
key = key.Replace(".", ":"); // 将.替换为:
string value = xmlNode.Attributes["value"].Value;
data[key] = value; // 解析xml文件,将结果扁平化映射到data中
}
this.Data = data; // 将扁平化的数据赋值到Data中
}
}
}
—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—
// MyConfigSource.cs
using Microsoft.Extensions.Configuration;
namespace MyConfigProviderDemo
{
/// <summary>
/// MyConfigSource用来提供参数。
/// </summary>
class MyConfigSource : FileConfigurationSource
{
public override IConfigurationProvider Build(IConfigurationBuilder builder)
{
EnsureDefaults(builder); // 这句代码老师没有具体说明,大意是对相对或者绝对路径进行转换的意思
return new MyConfigProvider(this);
}
}
}
—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—
// TestWebConfig.cs
using Microsoft.Extensions.Options;
using System;
namespace MyConfigProviderDemo
{
class TestWebConfig
{
private IOptionsSnapshot<WebConfig> optWebConfig;
public TestWebConfig(IOptionsSnapshot<WebConfig> optWebConfig)
{
this.optWebConfig = optWebConfig;
}
public void Test()
{
var wc = optWebConfig.Value;
Console.WriteLine(wc.Connstring.ConnectionString);
Console.WriteLine(wc.Connstring.ProviderName);
Console.WriteLine(wc.Conntext.ConnectionString);
Console.WriteLine(wc.Conntext.ProviderName);
Console.WriteLine(wc.AppStts.Name);
Console.WriteLine(wc.AppStts.Age);
Console.WriteLine(wc.AppStts.Proxy.Address);
Console.WriteLine(wc.AppStts.Proxy.Ids);
}
}
}
—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—
// WebConfig.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MyConfigProviderDemo
{
/*
* web.config文档
<configuration>
<connectionStrings>
<add name="connstring" connectionString="abc" providerName="mysql"/>
<add name="conntext" connectionString="123" providerName="sqlserver"/>
</connectionStrings>
<appSettings>
<add key="AppStts:name" value="smtp.test.com" />
<add key="AppStts:age" value="25" />
<add key="AppStts:proxy.address" value="127.0.0.1" />
<add key="AppStts:proxy:ids:0" value="666" />
<add key="AppStts:proxy:ids:1" value="777" />
</appSettings>
</configuration>
*
* 1. 将connectionStrings中的节点扁平化映射为:[cstr:{connectionString:"abc",providerName:"mysql"}, ctext:{connectionString:"123",providerName:"sqlserver"}]
*/
class ConnStrsAttr // 与connectionStrings节点中的connectionString和providerName对应
{
public string ConnectionString { get; set; } // 与扁平结构中connectionString对应
public string ProviderName { get; set; } // 与扁平结构中providerName对应
}
class AppSttsProxy // 与appSettings中proxy对应
{
public string Address { get; set; } // 与proxy:adde
public int[] Ids { get; set; } // 与proxy:ids:0和proxy:ids:1对应
}
class AppStts // 与appSettings节点对应
{
public string Name { get; set; } // 与<add key="name" value="smtp.test.com" />对应
public int Age { get; set; } // 与<add key="age" value="25" />对应
public AppSttsProxy Proxy { get; set; } // 与<add key = "proxy.../>对应
}
class WebConfig // 与web.config中所有节点对应
{
public ConnStrsAttr Connstring { get; set; } // 与扁平结构cstr对应
public ConnStrsAttr Conntext { get; set; } // 与扁平结构ctext对应
public AppStts AppStts { get; set; } // 与扁平结构appSettings对应
}
}
—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—
// web.config
<configuration>
<connectionStrings>
<add name="connstring" connectionString="abc" providerName="mysql"/>
<add name="conntext" connectionString="123" providerName="sqlserver"/>
</connectionStrings>
<appSettings>
<add key="AppStts:name" value="smtp.test.com" />
<add key="AppStts:age" value="25" />
<add key="AppStts:proxy.address" value="127.0.0.1" />
<add key="AppStts:proxy:ids:0" value="666" />
<add key="AppStts:proxy:ids:1" value="777" />
</appSettings>
</configuration>
—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—
// Program.cs
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
namespace MyConfigProviderDemo
{
class Program
{
static void Main(string[] args)
{
ServiceCollection services = new ServiceCollection();
services.AddScoped<TestWebConfig>();
ConfigurationBuilder configBuilder = new ConfigurationBuilder();
// configBuilder.Add(new MyConfigSource() { Path = "web.config" }); // 不加扩展方法用这种
configBuilder.AddMyConfig(); // 加了扩展方法可以直接.出来
IConfiguration configRoot = configBuilder.Build();
services.AddOptions().Configure<WebConfig>(e => configRoot.Bind(e)); // 根节点和WebConfig绑定
using (var sp = services.BuildServiceProvider())
{
var c = sp.GetRequiredService<TestWebConfig>();
c.Test();
}
}
}
}
控制台输出:
abc
mysql
123
sqlserver
smtp.test.com
25
127.0.0.1
System.Int32[]
2.3 项目总结
- 把DI和配置系统整个复习了一遍,查漏补缺之后再看这个项目感觉也还好。战术后仰。
结尾
书籍:ASP.NET Core技术内幕与项目实战
视频:https://www.bilibili.com/video/BV1pK41137He
著:杨中科
ISBN:978-7-115-58657-5
版次:第1版
发行:人民邮电出版社
※敬请购买正版书籍,侵删请联系85863947@qq.com※
※本文章为看书或查阅资料而总结的笔记,仅供参考,如有错误请留言指正,谢谢!※

浙公网安备 33010602011771号