在.config文件中使用自定义配置节
为了更清晰方便的配置自己的程序,可以考虑在app.config/web.config中添加自定义配置节,以添加如下配置节为例:
<configuration>
<ReportingSection>
<ReportScenario Name="ReportScenario A">
<ReportComponent Name="Component1" Sequence="1"/>
<ReportComponent Name="Component2" Sequence="2"/>
</ReportScenario>
</ReportingSection>
</configuration>
该节主要有3部分构成:
- ReportingSection作为根;
- ReportScenario作为有含义的节点,具有Name属性;
- ReportComponent作为ReportScenario的子节点。
另外,一个ReportScenario节点可以包含多个ReportComponent节点。
那么,如何实现读取这个自定义配置节呢?则需要继承3个类来共同完成。
1.ConfigurationSection
public class ReportScenarioSection : ConfigurationSection
{
[ConfigurationProperty("Name", IsRequired=true)]
public string Name
{
get
{
return this["Name"] as string;
}
set
{
this["Name"] = value;
}
}
}
ReportScenarioSection用于对应读取ReportingSection/ReportScenario节点,类的Name属性映射为ReportScenario节点的Name属性。实现这个映射的关键即为下面这句:
[ConfigurationProperty("Name", IsRequired=true)]
其中,"Name"指定了节点的属性名,IsRequeired=true指明了节点必须包含该属性。
2.ConfigurationElement
public class ReportComponentElement : ConfigurationElement
{
[ConfigurationProperty("Name", IsRequired=true)]
public string Name
{
get
{
return this["Name"] as string;
}
set
{
this["Name"] = value;
}
}
[ConfigurationProperty("Sequence")]
public int Sequence
{
get
{
int i;
int.TryParse(this["Sequence"].ToString(), out i);
return i;
}
set
{
this["Sequence"] = value;
}
}
}
ReportComponentElement用于读取每一个ReportingSection/ReportScenario/ReportComponent节点,由于不是配置节的入口,所以应该继承ConfigurationElement类,其余部分基本与上条一致,需要注意的是Sequence的类型不是一般的string,而是int。
3.ConfigurationElementCollection
这个类是最关键的,因为只有通过它才能实现子节点集合,否则子节点的名字是不能重复的!
public class ReportComponentCollection : ConfigurationElementCollection
{
/// <summary>
/// 必须实现的虚方法
/// </summary>
/// <returns>获得一个空节点</returns>
protected override ConfigurationElement CreateNewElement()
{
return new ReportComponentElement();
}
/// <summary>
/// 必须实现的虚方法
/// </summary>
/// <param name="element">ReportComponentCollection类型的节点</param>
/// <returns>节点的Key</returns>
protected override object GetElementKey(ConfigurationElement element)
{
if (element == null)
{
throw new ArgumentNullException("element");
}
var e = element as ReportComponentElement;
if (e == null)
{
throw new ArgumentException("Element should be a ReportComponentElement", "element");
}
return e.Name;
}
/// <summary>
/// 如果子节点名称需要自定义,则应重写此属性(同时还要重写ElementName属性)并返回BasicMap,否则子节点名称只能为add。
/// </summary>
public override ConfigurationElementCollectionType CollectionType
{
get
{
return ConfigurationElementCollectionType.BasicMap;
}
}
/// <summary>
/// 指定子节点名称
/// </summary>
protected override string ElementName
{
get
{
return "ReportComponent";
}
}
/// <summary>
/// 实现自定义索引器
/// </summary>
/// <param name="index">集合中的位置</param>
/// <returns></returns>
public ReportComponentElement this[int index]
{
get
{
return BaseGet(index) as ReportComponentElement;
}
set
{
if (BaseGet(index) != null)
{
BaseRemoveAt(index);
}
BaseAdd(index, value);
}
}
/// <summary>
/// 实现自定义索引器
/// </summary>
/// <param name="name">节点的Key</param>
/// <returns></returns>
public new ReportComponentElement this[string name]
{
get
{
return BaseGet(name) as ReportComponentElement;
}
}
}
这样就实现了集合的访问,不过目前还无法通过ReportScenarioSection访问这个集合,下面为ReportScenarioSection添加集合的属性。
public class ReportScenarioSection : ConfigurationSection
{
[ConfigurationProperty("Name", IsRequired=true)]
public string Name
{
get
{
return this["Name"] as string;
}
set
{
this["Name"] = value;
}
}
[ConfigurationProperty("", IsDefaultCollection=true)]
public ReportComponentCollection Components
{
get
{
return this[""] as ReportComponentCollection;
}
}
}
是不是觉得this[""]有点奇怪啊,这是因为子节点的路径为ReportingSection/ReportScenario/ReportComponent,如果把配置文件换成下面的方式则必须指定一个名称:
<ReportingSection>
<ReportScenario Name="ReportScenario A">
<Components>
<ReportComponent Name="Component1" Sequence="1"/>
<ReportComponent Name="Component2" Sequence="2"/>
</Components>
</ReportScenario>
</ReportingSection>
相应的代码为:
[ConfigurationProperty("Components", IsDefaultCollection = true)]
public ReportComponentCollection Components
{
get
{
return this["Components"] as ReportComponentCollection;
}
}
OK,现在读取自定义配置节的类都已经实现,还需要在.config中添加上注册文本,指明使用我们的类来加载配置节才行,其中ConfigFileTest是我的名称空间及程序集名。
<configuration>
<configSections>
<sectionGroup name="ReportingSection">
<section name="ReportScenario" type="ConfigFileTest.ReportScenarioSection,ConfigFileTest"/>
</sectionGroup>
</configSections>
</configuration>
这里的sectionGroup指明了第一层节点名称为ReportingSection;section指明了第二层节点名称为ReportScenario,同时指明了使用ConfigFileTest.ReportScenarioSection类来读取该节点,而该类在程序集ConfigFileTest中。
OK,准备工作就全部完成,下面就可以直接读取自定义配置节了:
var obj = ConfigurationManager.GetSection("ReportingSection/ReportScenario") as ReportScenarioSection;