自定义配置信息的高级应用
通过上篇博文对简单的自定义配置信息的学习,使得更加灵活的控制系统配置信息。实际项目中,这种配置的灵活度往往无法满足项目的灵活度和扩展性。
比如,一个配置信息有三部分组成,而每部分中有包括一些配置信息。仅仅使用简单的自定义配置无法满足,因此,需要提供更多的自定义配置方法来灵活实现。
针对配置信息中包括配置列表和配置项的要求,主要使用.Net Framework中的以下两个类来实现:
ConfigurationElement:配置文件中的一个配置项
ConfigurationElementCollection:配置文件中的一个配置项集合
ConfigurationSection:配置文件中的一个配置节信息。
使用下面的表格能够更好说明三者之间的关系:
ConfigurationSection |
||
1:N |
ConfigurationElementCollection |
|
1:N |
ConfigurationElement <add …/> |
|
ConfigurationElement <add …/> |
||
ConfigurationElement <add …/> |
针对以上知识点,使用一个项目实例逐渐展开。
项目需求:
库位管理系统需要实现仓库的全方位展示,设计多种数据(SQLserver、Oracle、MySQL等),每个数据库连接字符串的加密方式存在无法统一的情况。
配置文件的规划:
针对以上需求,对数据库配置文件信息的规划为:
DBConnectionConfiguration(ConfigurationSection) |
|
||||||||||
|
ConnectionStrings(ConfigurationElementCollection) |
|
|||||||||
|
ConnectionString1(ConfigurationElement) |
|
|||||||||
name |
description |
connectionString |
providerName |
connectionDecryptName |
|
||||||
ConnectionString2(ConfigurationElement) |
|
||||||||||
name |
description |
connectionString |
providerName |
connectionDecryptName |
|||||||
DataProviders(ConfigurationElementCollection) |
|
||||||||||
|
DataProvider1(ConfigurationElement) |
|
|||||||||
name |
description |
type |
|
||||||||
DataProvider2(ConfigurationElement) |
|
||||||||||
name |
description |
type |
|
||||||||
ConnectionDecrypts(ConfigurationElementCollection) |
|
||||||||||
|
ConnectionDecrypt(ConfigurationElement) |
|
|||||||||
name |
description |
type |
|
||||||||
ConnectionDecrypt(ConfigurationElement) |
|
||||||||||
name |
description |
type |
|
配置信息实体的定义:
针对以上配置信息,先对配置信息对应的实体进行设计:
1. DBConnectionConfiguration类
它作为配置文件中的一个配置节存在,配置节名称定义为TT.connectionManager,需要继承自ConfigurationSection类,同时,它还需要定义三个属性,分别是ConnectionStrings、DataProviders、ConnectionDecrypts,这三个属性都是列表信息,因此是ConfigurationElementCollection的自定义子类。
1 /// <summary> 2 /// 数据库连接配置信息 3 /// </summary> 4 public class DBConnectionConfiguration : ConfigurationSection 5 { 6 private const string SECION_NAME = "TT.connectionManager"; 7 8 /// <summary> 9 /// 获取数据库连接配置信息 10 /// </summary> 11 public static DBConnectionConfiguration GetConfig() 12 { 13 var config = ConfigurationManager.GetSection(SECION_NAME) as DBConnectionConfiguration; 14 return config; 15 } 16 17 /// <summary> 18 /// 数据库连接字符串配置集合 19 /// </summary> 20 [ConfigurationProperty("connectionStrings")] 21 public ConnectionStringCollection ConnectionStrings 22 { 23 get 24 { 25 return (ConnectionStringCollection)base["connectionStrings"]; 26 } 27 } 28 29 /// <summary> 30 /// DataProvider配置集合 31 /// </summary> 32 [ConfigurationProperty("dataProviders", IsRequired = true)] 33 public DataProviderCollection DataProviders 34 { 35 get 36 { 37 return (DataProviderCollection)base["dataProviders"]; 38 } 39 } 40 41 /// <summary> 42 /// 连接字符串加密方式 43 /// </summary> 44 [ConfigurationProperty("connectionDecrypts", IsRequired = false)] 45 public ConnectionDecryptCollection ConnectionDecrypts 46 { 47 get 48 { 49 return (ConnectionDecryptCollection)base["connectionDecrypts"]; 50 } 51 } 52 }
2. ConnectionString类
由于定义ConfigurationElementCollection的子类时,需要使用泛型方式,定义其包含的配置项的类型,因此,先定义配置项的实体。
由于,三个不同的配置项派生类都需要在配置信息中定义名称和描述信息来标识配置项信息,因此,提取出配置项基类,使用的配置项实体都继承自该基类,不同的配置信息在各自类中进行自定义。
1 /// <summary> 2 /// 以名字为键值的配置项 3 /// </summary> 4 public class NamedConfigurationElement : ConfigurationElement 5 { 6 /// <summary> 7 /// 名称 8 /// </summary> 9 [ConfigurationProperty("name", IsRequired = true, IsKey = true)] 10 public virtual string Name 11 { 12 get 13 { 14 return (string)this["name"]; 15 } 16 } 17 18 /// <summary> 19 /// 描述 20 /// </summary> 21 [ConfigurationProperty("description", DefaultValue = "")] 22 public virtual string Description 23 { 24 get 25 { 26 return (string)this["description"]; 27 } 28 } 29 }
以ConnectionStringElement为例,该实体定义三个属性信息:数据库连接字符串信息、数据库访问提供者、连接加密信息名称,其中连接加密名称填写时,使用ConnectionDecryptCollection中的定义解密类的类型信息进行反射实例化进行解密,不填写时则直接使用连接字符串信息。
1 /// <summary> 2 /// 连接字符串配置项 3 /// </summary> 4 public class ConnectionStringElement:NamedConfigurationElement 5 { 6 /// <summary> 7 /// 连接字符串 8 /// </summary> 9 private string _connectionString = ""; 10 11 /// <summary> 12 /// 连接字符串 13 /// </summary> 14 [ConfigurationProperty("connectionString")] 15 public string ConnectionString 16 { 17 get 18 { 19 if (!string.IsNullOrWhiteSpace(_connectionString)) 20 return _connectionString; 21 var decryptName = ConnectionDecryptName; 22 if (string.IsNullOrWhiteSpace(decryptName)) 23 _connectionString = (string)base["connectionString"]; 24 else 25 { 26 DBConnectionConfiguration config = DBConnectionConfiguration.GetConfig(); 27 var decrytType = config.ConnectionDecrypts[decryptName].Type; 28 IConnectionDecrypt cb = ReflectionHelper.CreateInstance(decrytType) as IConnectionDecrypt; 29 _connectionString = cb.Decrypt((string)base["connectionString"]); 30 } 31 return _connectionString; 32 } 33 } 34 35 /// <summary> 36 /// DataProvider名称 37 /// </summary> 38 [ConfigurationProperty("providerName")] 39 public string ProviderName 40 { 41 get 42 { 43 return (string)base["providerName"]; 44 } 45 } 46 47 48 /// <summary> 49 /// 连接字符串加密方法名称 50 /// </summary> 51 [ConfigurationProperty("connectionDecryptName", DefaultValue = "", IsRequired = false)] 52 public string ConnectionDecryptName 53 { 54 get 55 { 56 return (string)base["connectionDecryptName"]; 57 } 58 } 59 }
1 /// <summary> 2 /// DataProvider配置项 3 /// </summary> 4 public class DataProviderElement: NamedConfigurationElement 5 { 6 /// <summary> 7 /// DataProvider类型信息 8 /// </summary> 9 [ConfigurationProperty("type")] 10 public string Type 11 { 12 get 13 { 14 return (string)base["type"]; 15 } 16 } 17 }
1 /// <summary> 2 /// 字符串加密方式配置项 3 /// </summary> 4 public class ConnectionDecryptElement:NamedConfigurationElement 5 { 6 /// <summary> 7 /// 字符串加密方式类型信息 8 /// </summary> 9 [ConfigurationProperty("type")] 10 public string Type 11 { 12 get 13 { 14 return (string)base["type"]; 15 } 16 } 17 }
3. ConnectionStrings类
ConnectionStrings、DataProviders、ConnectionDecrypts三个配置列表实体,都会使用到使用配置项的名称获取对应的配置信息,因此需要将公共方法提取出来基类。
1 public abstract class NamedConfigurationElementCollection<T> : ConfigurationElementCollection 2 3 where T : NamedConfigurationElement, new() 4 5 { 6 7 /// <summary> 8 /// 按照名称获取指定的配置元素 9 /// </summary> 10 /// <param name="name">名称</param> 11 /// <returns>配置元素</returns> 12 public new T this[string name] { get { return (T)BaseGet(name); } } 13 14 15 /// <summary> 16 /// 是否包含指定的配置元素 17 /// </summary> 18 /// <param name="name">配置元素名称</param> 19 /// <returns>是否包含</returns> 20 public bool ContainsKey(string name) { return BaseGet(name) != null; } 21 22 /// <summary> 23 /// 添加元素 24 /// </summary> 25 /// <param name="obj"></param> 26 public virtual void Add(T obj) 27 { 28 BaseAdd(obj); 29 } 30 31 /// <summary> 32 /// 得到元素的Key值 33 /// </summary> 34 /// <param name="element">配置元素</param> 35 /// <returns>配置元素所对应的配置元素</returns> 36 protected override object GetElementKey(ConfigurationElement element) { return ((T)element).Name; } 37 38 /// <summary> 39 /// 生成新的配置元素实例 40 /// </summary> 41 /// <returns>配置元素实例</returns> 42 protected override ConfigurationElement CreateNewElement() { return new T(); } 43 }
定义基类后,ConnectionStrings、DataProviders、ConnectionDecrypts三个配置列表实体只需要继承该基类,不需要任何实现。
1 public class ConnectionStringCollection: NamedConfigurationElementCollection<ConnectionStringElement> 2 { 3 4 5 6 } 7 8 public class DataProviderCollection: NamedConfigurationElementCollection<DataProviderElement> 9 { 10 11 } 12 13 public class ConnectionDecryptCollection: NamedConfigurationElementCollection<ConnectionDecryptElement> 14 { 15 16 17 }
通过以上配置实体类的定义,就可以使用以下方式获取到数据库配置信息:
DBConnectionConfiguration settings = DBConnectionConfiguration.GetConfig();
然后,根据数据库连接的名称获取到对应的信息,比如:
var connectionString = settings.ConnectionStrings[dbName].ConnectionString;
这里的DBName就是connectString配置项中的name信息。