为扩展程序提供可自定义的配置文件的方法(二)

    上次介绍了还有另外两种自定义配置文件的办法,这次介绍WCF所采用的方法。

    请看配置文件:

代码
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="extensions" type="CustomConfiguration.Configuration.ExtensionConfigurationSection, CustomConfiguration"/>
<section name="custom" type="CustomConfiguration.Configuration.CustomConfigurationSection, CustomConfiguration"/>
</configSections>
<extensions>
<add name="smtp" type="CustomConfiguration.Configuration.SmtpSenderElement, CustomConfiguration" />
<add name="sms" type="CustomConfiguration.Configuration.SenderElement, CustomConfiguration" />
</extensions>
<custom>
<senders>
<smtp name="smtp" semaphore="10" type="CustomConfiguration.SmtpSender, CustomConfiguration">
<authorization userName="abcd" userPassword="1234" />
</smtp>
<sms name="sms" semaphore="1" type="CustomConfiguration.SmsSender, CustomConfiguration"/>
</senders>
</custom>
</configuration>

 

     

    有两个自定义节点,extensions和custom,其中extensions定义了扩展类所使用自定义配置文件的处理类,当程序在custom节点中遇到自定义的节点,就会使用这里定义好的类来处理,这是扩展类自定义配置文件的核心。custom是我们的示例节点,定义了一个senders属性,这是一个集合,里面定义了实现了ISender接口的类,以及实例化这个类所需要的相关配置。

internal interface ISender
{
bool Send(string message);
void ApplyConfiguration(Configuration.SenderElement element);
}

 

     其中Send方法是业务方法,ApplyConfiguration方法不是必须的,因为实现类可以自行通过访问配置文件来获得自己的配置信息,这里为了简单起见,定义了一个方法,方便使用。

      核心类SenderCollection,这是一个配置类,处理custom节点的senders子节点,继承自ConfigurationElement。虽然是一个Collection,但是并没有继承自ConfigurationElementCollection,原因是ConfigurationElementCollection只能创建同一类型的元素集合,要想创建不同类型的集合,只能自己实现,这也就是本文的重点。代码如下:

代码
internal sealed class SenderCollection : ConfigurationElement, IEnumerable<SenderElement>
{
private readonly List<SenderElement> m_Elements = new List<SenderElement>();

protected override void DeserializeElement(System.Xml.XmlReader reader, bool serializeCollectionKey)
{
if (reader.HasAttributes && (0 < reader.AttributeCount))
{
while (reader.MoveToNextAttribute())
{
if (this.Properties.Contains(reader.Name))
{
base[reader.Name] = this.Properties[reader.Name].Converter.ConvertFromString(reader.Value);
}
else
{
this.OnDeserializeUnrecognizedAttribute(reader.Name, reader.Value);
}
}
}
if (XmlNodeType.Element != reader.NodeType)
{
reader.MoveToElement();
}
XmlReader reader2
= reader.ReadSubtree();
if (reader2.Read())
{
while (reader2.Read())
{
if (XmlNodeType.Element == reader2.NodeType)
{
SenderElement element
= this.CreateNewSection(reader2.Name);
m_Elements.Add(element);
element.DeserializeInternal(reader2,
false);
}
}
}
}

private SenderElement CreateNewSection(string name)
{
var typeName
= ExtensionConfigurationSection.Current.Extensions.GetElement(name);
var local
= (SenderElement)Activator.CreateInstance(Type.GetType(typeName));
local.InternalInitializeDefault();
return local;
}
#region IEnumerable<SenderElement> 成员

public IEnumerator<SenderElement> GetEnumerator()
{
return m_Elements.GetEnumerator();
}

#endregion

#region IEnumerable 成员

IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}

#endregion

 

      重载了DeserializeElement方法,自己来实现了xml文件的解析,中间的关键步骤在于每读到一个子节点,就调用CreateNewSection方法来创建一个自定义的配置元素对象,创建的方法是通过查找extensions节点来获得自定义配置元素的类型,然后创建这个类型的实例化对象,并且调用初始化方法。创建完毕以后调用DeserializeInternal方法来让配置文件自己处理自己的节点,这是重用System.Configuration框架的关键,通过这个可以采用声明式的方法来定义自己的配置类,而无需像上篇文章一样手动处理XmlRead。由于CreateNewSection需要通过查找来获得对应配置类,这也是为什么要把extensions独立出来一个配置节点,否则由于当前配置节点还没有处理完毕,这个时候访问当前配置节点,会造成循环调用,结果就是堆栈溢出。

      Main方法如下,循环遍历senders,构造对象,调用send方法测试:

代码
static void Main(string[] args)
{
foreach (SenderElement e in Configuration.CustomConfigurationSection.Current.Senders)
{
ISender sender
= (ISender)Activator.CreateInstance(Type.GetType(e.Type));
sender.ApplyConfiguration(e);
sender.Send(
"Hello world!");
}
Console.ReadLine();
}

 

 

      示例项目文件下载

posted @ 2010-08-11 11:09  wenhx  阅读(562)  评论(0编辑  收藏  举报