在大型项目开发中,我们需要配置各种各样的信息。比如:在线支付方式有支付宝,网银,财付通,快钱。我们需要配置支付宝账号信息,需要配置财付通账号信息,需要配置网银账号信息还有快钱账号信息。发邮件呢?我们需要配置邮件服务器信息。发短信呢?我们需要配置短息服务器信息。对于各种各样形形色色的配置信息,我们该如何配置呢?又该如何才能方便管理呢?我们当然希望有一个通用的配置管理模块来管理我们的配置信息,方便我们定位配置信息,方便修改,而且修改配置信息不会引起站点的重启等等。
好了,我先介绍下.Net自带的配置管理,然后在使用反射技术定义一个通用配置模块。
1)使用System.Configuration.AppSettingsSection类
.Net已经提供了AppSettingsSection类方便我们配置信息。我们可以把配置信息放在Web.confg或App.config的appSettings节点下。如下:
<configuration>
<appSettings>
<!--支付宝合作身份者Id-->
<add key="AlipayId" value="1234567890"/>
<add key="AlipayKey" value="test1234567890"/>
<add key="AlipayEmail" value="yangyanping0615@163.com"/>
<!--财付通商户号-->
<add key="TenpayId" value="1234567890"></add>
<!--财付通密钥-->
<add key="TenpayKey" value="test1234567890"/>
<!--网银商户号-->
<add key="ChinaBankId" value="1001"></add>
<!--网银密钥-->
<add key="ChinaBankKey" value="test"/>
</appSettings>
</configuration>
这种配置方式是.Net最基本的配置方式,也是我们最常用的配置方式,但这样的配置方式显然不能让我们满意。因为大量的配置信息放在一起,即无法对信息进行分组,也不方便管理,时间一长,我们自己可能已经不记得配置信息的意思,而且修改任何的配置内容都会导致Asp.Net站点重新启动。
2)使用System.Configuration.NameValueSectionHandler类
.Net在System下提供NameValueSectionHandler类可以对配置信息进行方便的分组。NameValueSectionHandler类的定义是这样的:
public class NameValueSectionHandler : IConfigurationSectionHandler { // Fields private const string defaultKeyAttribute = "key"; private const string defaultValueAttribute = "value"; // Methods [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] public NameValueSectionHandler(); public object Create(object parent, object context, XmlNode section); internal static object CreateStatic(object parent, XmlNode section); internal static object CreateStatic(object parent, XmlNode section, string keyAttriuteName, string valueAttributeName); // Properties protected virtual string KeyAttributeName { get; } protected virtual string ValueAttributeName { get; } }
NameValueSectionHandler类实现了接口IConfigurationSectionHandler的Create方法,返回一个ReadOnlyNameValueCollection
internal static object CreateStatic(object parent, XmlNode section, string keyAttriuteName, string valueAttributeName) { ReadOnlyNameValueCollection values; if (parent == null) { values = new ReadOnlyNameValueCollection(StringComparer.OrdinalIgnoreCase); } else { ReadOnlyNameValueCollection values2 = (ReadOnlyNameValueCollection) parent; values = new ReadOnlyNameValueCollection(values2); } HandlerBase.CheckForUnrecognizedAttributes(section); foreach (XmlNode node in section.ChildNodes) { if (!HandlerBase.IsIgnorableAlsoCheckForNonElement(node)) { if (node.Name == "add") { string str = HandlerBase.RemoveRequiredAttribute(node, keyAttriuteName); string str2 = HandlerBase.RemoveRequiredAttribute(node, valueAttributeName, true); HandlerBase.CheckForUnrecognizedAttributes(node); values[str] = str2; } else { if (node.Name == "remove") { string name = HandlerBase.RemoveRequiredAttribute(node, keyAttriuteName); HandlerBase.CheckForUnrecognizedAttributes(node); values.Remove(name); continue; } if (node.Name.Equals("clear")) { HandlerBase.CheckForUnrecognizedAttributes(node); values.Clear(); continue; } HandlerBase.ThrowUnrecognizedElement(node); } } } values.SetReadOnly(); return values; }
从代码中,可以看出需要一个Add节点,该节点包含一个Key和Value的属性。我们可以这样配置各种信息。
<configuration> <configSections> <section name="Alipay" type="System.Configuration.NameValueSectionHandler"/> <section name="TenPay" type="System.Configuration.NameValueSectionHandler"/> <section name="WebBank" type="System.Configuration.NameValueSectionHandler"/> </configSections> <Alipay> <!--合作身份者ID--> <add key="Id" value="1234567890" /> <!--安全检验码--> <add key="key" value="test1234567890" /> <add key="email" value="alipay-test03@alipay.com" /> </Alipay> <TenPay> <!--合作身份者ID--> <add key="Id" value="1234567890" /> <!--安全检验码--> <add key="key" value="test1234567890" /> </TenPay> <WebBank> <!--合作身份者ID--> <add key="Id" value="1001" /> <!--安全检验码--> <add key="key" value="test1234567890" /> </WebBank> </configuration>
可以这样读取配置信息:
//支付宝配置信息
var alipays = (NameValueCollection)ConfigurationManager.GetSection("Alipay");
//支付宝Id
string alipayId = alipays["Id"];
//支付宝Key
string alipaykey = alipays["key"];
这种配置方式可以有效的对各种不同的配置信息进行分组,方便查看和修改。但不符合面向对象的开发,也无法对配置的信息进行类型检查,修改配置信息一样会导致Asp.Net站点重新启动。
3)自定义配置节点
我们可以仿照AppSettingsSection类的实现方式,自定义节点配置类。可以继承ConfigurationSection类实现自定义节点配置类。
public class AlipaySetion : ConfigurationSection { [ConfigurationProperty("Id", IsKey = true)] public string Id { get { return (string)base["Id"]; } set { base["Id"] = value; } } [ConfigurationProperty("Key")] public string Key { get { return (string)base["Key"]; } set { base["Key"] = value; } } [ConfigurationProperty("Email")] public string Email { get { return (string)base["Email"]; } set { base["Email"] = value; } } }
节点配置如下:
<configuration>
<configSections>
<section name="Alipay" type="ConsoleApplication2.AlipaySetion,ConsoleApplication2"/>
</configSections>
<Alipay Id="1234567890" Key="test1234567890" Email="alipay-test03@alipay.com"/>
</configuration>
读取方式:
var alipays = (AlipaySetion)ConfigurationManager.GetSection("Alipay");
string id = alipays.Id;
string key = alipays.Key;
自定义配置节点方便查看和管理,也符合面向对象开发和进行类型检查。但我们需要写很多ConfigurationSection类的实现,显然无法满足我们的需求。
4)通用的配置模块
.Net中使用反射技术可以动态的创建一个对象,可以调用对象的方法,可以给对象属性赋值。我们是不是可以根据这个特点动态的创建配置类,然后给配置类的属性赋值呢?那需要什么呢?需要2个基础文件,一个是对象的映射关系文件,另一个是对象属性映射文件,如图:
首先来看看对象的映射关系文件(文件名:System.config),内容如下:
<configuration> <configMappings> <mapping AssemblyName="ConsoleApplication2" ClassName="ConsoleApplication2.AlipayConfiguration" Name="AlipayConfiguration" section="AlipayConfiguration" fileName="Mapping.AlipayConfiguration.config"> <field xmlName="Id" fieldName="Id" /> <field xmlName="Key" fieldName="Key" /> <field xmlName="Email" fieldName="Email" /> </mapping> <mapping AssemblyName="ConsoleApplication2" ClassName="ConsoleApplication2.TenpayConfiguration" Name="TenpayConfiguration" section="TenpayConfiguration" fileName="Mapping.TenpayConfiguration.config"> <field xmlName="Id" fieldName="Id" /> <field xmlName="Key" fieldName="Key" /> </mapping> </configMappings> </configuration>
对象属性映射文件:
支付宝配置类文件(Mapping.AlipayConfiguration.config)
<?xml version="1.0" encoding="utf-8" ?>
<AlipayConfiguration>
<node name="Id" value="1234567890"/>
<node name="Key" value="test1234567890"/>
<node name="Email" value="alipay-test03@alipay.com" />
</AlipayConfiguration>
Tenpay配置类文件(Mapping.TenpayConfiguration.config)
<?xml version="1.0" encoding="utf-8" ?>
<TenpayConfiguration>
<node name="Id" value="1234567890"/>
<node name="Key" value="test1234567890"/>
</TenpayConfiguration>
xml文件我就不做过多说明了,很简单。看到节点名称基本就能猜出其意思。下面来设计映射类和配置管理类,配置管理类顾名思义就能知道是管理所有配置类的。那怎样才能管理所有的配置类呢?我们知道object是所有对象的基类。当然我们的配置类也是继承object的。我们可以设计一个键值对的集合来存放所有的配置信息。比如:private Dictionary<string, object> Mappings。其中的keyi就是配置的类名,value就是配置对象。我们先看看对象的映射类的定义:
public class ConfigMapping { private Dictionary<string, string> fieldMappings; /// <summary> /// 配置名(唯一) /// </summary> public string Name { get; set; } /// <summary> /// 配置对象程序集名 /// </summary> public string AssemblyName { get; set; } /// <summary> /// 配置对象类名 /// </summary> public string ClassName { get; set; } /// <summary> /// 配置对象文件 /// </summary> public string ConfigFile { get; set; } /// <summary> /// 类型 /// </summary> private Type type; /// <summary> /// 配置对象类型 /// </summary> public Type Type { get { return Type.GetType(ClassName + "," + AssemblyName); } set { type = value; Instance = Activator.CreateInstance(Type.GetType(ClassName + "," + AssemblyName)); } } /// <summary> /// 配置对象实例 /// </summary> public object Instance { get; private set; } //域集合 public Dictionary<string, string> FieldMappings { get { return fieldMappings ?? (new Dictionary<string, string>()); } set { fieldMappings = value; } } }
配置管理类ConfigManager代码:
public class ConfigManager { private static readonly object sync = new object(); private static ConfigManager manager; private Dictionary<string, object> Mappings; private const string configFile = "Configuration/System.config"; private ConfigManager() { Mappings = new Dictionary<string, object>(); } public static ConfigManager Instance { get { lock (sync) { if (manager == null) { lock (sync) { manager = new ConfigManager(); manager.Init(); } } } return manager; } } /// <summary> /// 初始化配置管理 /// </summary> privatevoid Init() { string path = AppDomain.CurrentDomain.BaseDirectory + configFile; var document = new XmlDocument(); document.Load(path); var nodes = document.SelectNodes("configuration/configMappings/mapping"); foreach (XmlNode node in nodes) { var mapping = new ConfigMapping { Name = node.Attributes["Name"].InnerText, AssemblyName = node.Attributes["AssemblyName"].InnerText, ClassName = node.Attributes["ClassName"].InnerText, ConfigFile = node.Attributes["fileName"].InnerText, Type = Type.GetType(node.Attributes["ClassName"].InnerText + "," + node.Attributes["AssemblyName"].InnerText) }; foreach (XmlNode childNode in node.SelectNodes("field")) { mapping.FieldMappings.Add(childNode.Attributes["xmlName"].InnerText, childNode.Attributes["fieldName"].InnerText); } SetProperty(mapping); Mappings.Add(mapping.Name, mapping.Instance); } } /// <summary> /// 设置对象属性值 /// </summary> /// <param name="mapping"></param> private void SetProperty(ConfigMapping mapping) { var xml = new XmlDocument(); xml.Load(AppDomain.CurrentDomain.BaseDirectory + "Configuration/" + mapping.ConfigFile); var ns = xml.SelectSingleNode(mapping.Name); var infos = mapping.Instance.GetType().GetProperties(); foreach (var info in infos) { foreach (XmlNode n in ns.ChildNodes) { if (n.Attributes["name"].Value == info.Name) { object value = Convert.ChangeType(n.Attributes["value"].Value, info.PropertyType); info.SetValue(mapping.Instance, value, null); } } } } /// <summary> /// 获取配置类实例 /// </summary> /// <param name="name"></param> /// <returns></returns> public object GetMappingByName(string name) { return Mappings[name]; } }
读取配置信息很简单,代码如下:
var alipay = (AlipayConfiguration) ConfigManager.Instance.GetMappingByName("AlipayConfiguration");
long alipayId = alipay.Id;
string alipayKey = alipay.Key;
var tenpay = (TenpayConfiguration)ConfigManager.Instance.GetMappingByName("TenpayConfiguration");
long tenpayId = tenpay.Id;
string tenpayKey = tenpay.Key;
总结:这篇文章主要是介绍了AppSettingsSection类,NameValueSectionHandler类,自定义节点类,和设计一个配置管理类,来配置我们的各种信息。各有优缺点。大家在实际的项目开发中,根据网站配置信息多少,选择适合自己的配置方式。
好了,感谢阅读,希望这篇文章能给你带来帮助!