在配置我们的应用程序的时候,经常会遇到有的配置元素、配置节需要可变的属性。比如,在 .net 2.0 中经常遇到的 Provider 模式。在配置 Provider 的时候,可能会需要为不同的 Provider 提供不同的属性,比如为 SqlDataProvider 提供连接字符串属性,为 XmlFileDataProvider 提供文件路径等。
下面代码是一般的 ConfigurationElement 需要的代码:
public class ProviderSettings : ConfigurationElement2
{3
private static readonly ConfigurationProperty _propName;4
private static readonly ConfigurationProperty _propType;5
private static readonly ConfigurationPropertyCollection _props;6

7
static ProviderSettings()8
{9
_propName = new ConfigurationProperty("name", typeof(string), null, ConfigurationPropertyOptions.IsKey | ConfigurationPropertyOptions.Required);10
_propType = new ConfigurationProperty("type", typeof(string), null, null, new StringValidator(1, Int32.MaxValue), ConfigurationPropertyOptions.Required);11

12
_props = new ConfigurationPropertyCollection();13
_props.Add(_propName);14
_props.Add(_propType);15
}16
17
protected override ConfigurationPropertyCollection Properties18
{19
get { return _props; }20
}21

22
[ConfigurationProperty("name", RequiredValue = true, Options = ConfigurationPropertyOptions.IsKey)]23
[StringValidator(MinLength = 1)]24
public string Name25
{26
get { return (string)base[_propName]; }27
set { base[_propName] = value; }28
}29

30
[ConfigurationProperty("type", RequiredValue = true)]31
[StringValidator(MinLength = 1)]32
public string Type33
{34
get { return (string)base[_propType]; }35
set { base[_propType] = value; }36
}37
}
为了使 .net 2.0 配置系统允许我们使用自定义的属性(也就是除了已经定义的 name 和 type 属性),我们还需要做一些其他的工作。再看看 ConfigurationElement 的方法和属性,发现有 virtual 的 OnDeserializeUnrecognizedAttribute 和 OnDeserializeUnrecognizedElement 方法:
- OnDeserializeUnrecognizedAttribute: Called when an unknown attribute is encountered while deserializing the ConfigurationElement object
- OnDeserializeUnrecognizedElement: Called when an unknown sub element is encountered while deserializing the ConfigurationElement object
public ProviderSettings : ConfigurationElement2
{3
// Other members4
.5

6
private Dictionary<string, string> _params = new Dictionary<string, string>(StringComparer.InvariantCulture);7
8
/// <summary>9
/// All the undefined attributes.10
/// </summary>11
public Dictionary<string, string> Parameters12
{13
get { return _params; }14
}15
16
protected override bool OnDeserializeUnrecognizedAttribute(string name, string value)17
{18
Parameters[name] = value;19
return true;20
}21
}其对应的单元测试代码就不写了,大家自己写吧:)
完成了上面的步骤,其实还是不够的。如果单元测试代码写的好的话,马上就能发现问题了:) 使用上面的方法,在读取配置的时候是没有问题的,可以顺利得从 Parameters 中获取配置的数据。然而在保存的时候就出现问题了。在使用配置的时候,我们在应用程序无论怎么操作 Parameters,保存的时候都不能将改动保存到配置文件中;而且如果 ProviderSettings 中的已定义属性被更改过的话,在保存的时候那些 unrecognized attributes 也消失了。也就是我们只能从配置文件中读取属性而不能修改这些属性!这显然不满足我们的需求!不过从刚才的现象中似乎表示被保存的配置应该都是 Configuration.Properties 中的内容。看来我们还要想办法把那些 unrecognized attributes 放到 Properties 中才行啊。
下面就是改进以后的代码:
public ProviderSettings : ConfigurationElement2
{3
// Other members4
//5
// Note: Since _props will be updated in IsModified-UpdatePropertyCollection,6
// it cannot be static. And also, the static constructor will be modified.7
.8
9
private readonly ConfigurationPropertyCollection _props;10
private static readonly PredefinedAttributes = new string[]{ "name","type" };11
12
public ProviderSettings()13
{14
_props = new ConfigurationPropertyCollection();15
_props.Add(_propName);16
_props.Add(_propType);17
}18
19
protected override bool IsModified()20
{21
return (!UpdatePropertyCollection()) ? base.IsModified() : true;22
}23
24
private bool UpdatePropertyCollection()25
{26
bool isModified = false;27

28
List props = new List();29
foreach (ConfigurationProperty prop in Properties) {30
if (Array.BinarySearch(PredefinedAttributes, prop.Name) >= 0 ||31
parameters.ContainsKey(prop.Name)) {32
continue;33
}34

35
props.Add(prop.Name);36
isModified = true;37
}38

39
foreach (string name in props) {40
Properties.Remove(name);41
}42

43
foreach (KeyValuePair kvp in Parameters) {44
string t = GetProperty(kvp.Key);45
if (t == null || kvp.Value != t) {46
SetProperty(kvp.Key, kvp.Value);47
isModified = true;48
}49
}50

51
return isModified;52
}53

54
private bool SetProperty(string name, string value)55
{56
ConfigurationProperty prop = null;57
if (Properties.Contains(name)) {58
prop = Properties[name];59
}60
else {61
prop = new ConfigurationProperty(name, typeof(string), null);62
Properties.Add(prop);63
}64

65
if (prop != null) {66
base[prop] = value;67
return true;68
}69
else {70
return false;71
}72
}73

74
private string GetProperty(string name)75
{76
if (Properties.Contains(name)) {77
ConfigurationProperty prop = Properties[name];78
if (prop != null) {79
return base[prop] as string;80
}81
}82

83
return null;84
}85
}

浙公网安备 33010602011771号