首页 > 编程 > .NET > 正文

.NET 2.0 中的自定义配置处理

2024-07-10 13:09:46
字体:
来源:转载
供稿:网友
  引言

  现代软件开发中,各种技术、技巧越来越依赖配置,譬如客户端对用户体验的个性化设置、系统的各种运行时参数设置、可插拔的插件机制、基于配置的ioc架构模式等。配置方式也从最初的二进制存储格式逐步过度到ini文本格式直至今时所广泛使用的xml格式。使用xml格式进行配置,大大提高了对设置数据的表现能力,但是在 .net 1.x 中对xml配置的操控还有诸多不便,尤其是对xml配置的存储同步机制很不完善,而从 .net 2.0 开始,框架提供了更丰富和易于操控使用的机制。

  .net 中的配置文件(xml)必须以“<configuration>”为根节点,配置文件分为两大部分:配置声明区和数据设置区。

  配置声明区:位于<configuration><configsections>内,通过<section>节点进行声明定义。

  数据设置区:位于<configuration>根节点内除<configsections>以外的任意节点。

  数据设置区可以是用户定义的任意结构层次,但是其“根节点”必须预先在设置声明区定义,运行时会进行有效性检测,一旦发现没有声明的配置节点则会产生一个运行时配置异常。

  范例配置文件

<configuration>
<configsections>
<section name="datasystems" type="swsystem.data.configuration.datasystemssection, swsystem.data" />
</configsections>

<datasystems>
<datasystem name="imrp" currentprovider="sqlprovider">
<dataprovider name="mssqlprovider" type="swsystem.data.providers.sqldataprovider" datafile="d:/zongsoft/develop 2005/swsystem.data/services/swdataengine.xml" connectionstring="uid=sa;pwd=;initial catalog=imrp;data source=127.0.0.1" />
<dataprovider name="postgresqlprovider" type="swsystem.data.providers.postgredataprovider" datafile="d:/zongsoft/develop 2005/swsystem.data/services/swdataengine.xml" connectionstring="server=127.0.0.1;port=5432;user id=postgres;password=postgres;database=imrp;encoding=unicode;" />

<datamodules>
<add name="stockmodule" type="zongsoft.applications.imrp.business.stockmodule, zongsoft.applications.imrp.business" />
</datamodules>
</datasystem>
</datasystems>
</configuration>


  概述

  在.net 2.0中实现自定义配置,可以使用编程或声明性(属性化)代码编写模型创建自定义配置节。

  编程模型。此模型要求为每个节属性 (attribute) 创建一个用于获取和/或设置其值的属性 (property),并将其添加到基础 configurationelement 基类的内部属性 (property) 包中。

  声明模型。这种比较简单的模型也称为属性 (attribute) 化模型,允许您通过使用属性 (property) 来定义节属性 (attribute),并使用属性 (attribute) 对其进行修饰。

  配置文件中的元素称为 基本xml元素 或 节。基本元素只是具有相关属性(如果有)的简单 xml 标记。节最简单的形式与基本元素一致。而复杂的节可以包括一个或多个基本元素、元素的集合以及其他节。

  configurationproperty 类表示配置节点中的特性(attribute)或它的子元素,我们要做的就是通过 configurationproperty 类将配置实体类中的属性(property)映射到对应的节点特性(attribute)。

  配置声明

  要在配置文件中使用自定义的配置数据,必须在声明区通过<section>节点对自定义配置节处理程序进行声明,该节点有两个必需属性:name 和 type。

  name 属性:指定与 type 属性中指定的配置节处理程序关联的配置节或元素的名称。这是该元素在配置文件的设置区中使用的名称。

  type 属性:指定用来处理 name 属性所指定的设置节的处理程序。使用如下格式:
type="configurationsectionhandlerclass, assemblyfilename, version, culture, publickeytoken"
如果对处理程序的版本没要求,或者其为无区域和强签名的程序集,则可以省略后面的三项,否则必须严格匹配版本。注意:程序集文件名不要带其扩展文件名(通常为.dll)。

  范例配置文件中的配置声明定义如: <section name="datasystems"  type="swsystem.data.configuration.datasystemssection, swsystem.data" /> 则表示数据设置区中的<datasystems>节点由 swsystem.data.configuration.datasystemssection 类进行处理,并且该类位于 swsystem.data 程序集中。

  在 .net 1.x 中要实现自定义的配置处理程序类,其必须实现 iconfigurationsectionhandler 接口,现在 .net 2.0 中只需要将你的处理程序类继承自 configurationsection 类即可。其处理步骤大致如下:

  首先定义并创建一个 configurationpropertycollection 类的实例,用以保存配置节点的特性(attribute)以及它的所有子元素映射,当然你也可以使用 configurationelement 基类中的 properties 属性。

  然后创建 configurationproperty 类以映射到对应的节点特性(attribute)或子元素。

  在类型构造函数或实例构造函数中,将创建的 configurationproperty 类实例加入到已创建好的 configurationpropertycollection 集合中。

  最终,我们的范例配置处理程序类看起来可能是这样(如果你使用声明模式则代码看起来没有这么麻烦,这些配置属性类将由框架运行时帮你反射生成,正因为如此,所以它的运行时效率要差些。):

public class datasystemssection : configurationsection
{
 private static readonly configurationproperty _datasystems = new configurationproperty(null, typeof(datasystemelementcollection), null, configurationpropertyoptions.isdefaultcollection);
 private static configurationpropertycollection _properties = new configurationpropertycollection();

 static datasystemssection()
 {
  _properties.add(_datasystems);
 }

 public datasystemelementcollection datasystems
 {
  get
  {
   return (datasystemelementcollection)base[_datasystems];
  }
 }

 protected override configurationpropertycollection properties
 {
  get
  {
   return _properties;
  }
 }
}

  节点映射/配置实体

  为所有的配置节点创建对应的配置实体类,该类中的属性(property)对应节点中的特性(attribute)和子元素(集合)。其编写步骤和开发方式与处理程序类似,只是这时我们的基类变成了 configurationelement。

public class datasystemelement : configurationelement
{
 private static readonly configurationproperty _name = new configurationproperty("name", typeof(string), null, null, new stringvalidator(1), configurationpropertyoptions.iskey | configurationpropertyoptions.isrequired);
 private static readonly configurationproperty _currentprovider = new configurationproperty("currentprovider", typeof(string), string.empty, configurationpropertyoptions.isrequired);
 private static readonly configurationproperty _datamodules = new configurationproperty("datamodules", typeof(datamoduleelementcollection), null, configurationpropertyoptions.none);
 private static readonly configurationproperty _dataproviders = new configurationproperty(null, typeof(dataproviderelementcollection), null, configurationpropertyoptions.isrequired | configurationpropertyoptions.isdefaultcollection);

 private static configurationpropertycollection _properties = new configurationpropertycollection();

 #region 类型构造函数
 static datasystemelement()
 {
  _properties.add(_name);
  _properties.add(_currentprovider);
  _properties.add(_dataproviders);
  _properties.add(_datamodules);
 }
 #endregion

 #region 构造函数
 public datasystemelement()
 {}

 public datasystemelement(string name)
 {
  this.name = name;
 }

 public datasystemelement(string name, string currentprovider)
 {
  this.name = name;
  this.currentprovidername = currentprovider;
 }
 #endregion

 #region 公共属性
 public string name
 {
  get
  {
   return (string)base[_name];
  }
  set
  {
   base[_name] = value;
  }
 }

 public string currentprovidername
 {
  get
  {
   return (string)this[_currentprovider];
  }
  set
  {
   this[_currentprovider] = value;
  }
 }

 public datamoduleelementcollection datamodules
 {
  get
  {
   return (datamoduleelementcollection)base[_datamodules];
  }
 }

 public dataproviderelementcollection dataproviders
 {
  get
  {
   return (dataproviderelementcollection)base[_dataproviders];
  }
 }
 #endregion
}

  需要注意的是,<dataprovider> 和 <datamodules> 子元素处理方式的差异。

  <datamodules> 是个嵌套的集合节点,它下面有标准的 <add>, <remove>, <clear> 子元素,且 <datamodules> 元素不能在同一个 <datasystem> 节点下出现多次。

  <dataprovider> 是 <datasystem> 节点下的直接子节点集合,<dataprovider> 是可以在同一个父节点下出现多次的集合项节点,故需要对其映射的 configurationproperty 类构造函数中的 options 参数包含 configurationpropertyoptions.isdefaultcollection 枚举项(它指示.net框架构造一个嵌套节),另外,不能指定它对应的配置节点的名字,即必须保持构造函数中 name 参数的值为空引用([c#]null/[vb.net]nothing)或空字符串(string.empty/"")。

  节点集合

  创建派生自 configurationelementcollection 的类,在派生类中必须重写(override)的抽象方法:

protected abstract configurationelement createnewelement()

  在从配置文件加载集合时,会调用该方法以创建各个元素。重写该方法以创建特定类型的自定义 configurationelement 对象。

protected abstract object getelementkey(configurationelement element)

  在派生类中重写时获取指定配置元素的键值。

  对于非默认的节点集合类,还必须重写 collectiontype 和 elementname 只读属性的getter,其代码可能如下:

protected override string elementname
{
 get
 {
  return "dataprovider"; //"dataprovider" replace with your elementname in collection.
 }
}

public override configurationelementcollectiontype collectiontype
{
 get
 {
  return configurationelementcollectiontype.basicmap;
 }
}

  可以重写 throwonduplicate 只读属性的getter,以指示当向 configurationelementcollection 添加重复的 configurationelement 是否会导致引发异常。默认情况,只有当该元素的 collectiontype 值为 addremoveclearmap 或 addremoveclearmapalternate 时才会引发异常,如果希望非默认节点集合不接受重复项(通常如此),那么就必须重写该属性的getter,始终返回真(true)。

  请注意,键和值都相同的元素不会被视为重复元素,而是接受此类元素且不出现提示,只有键相同而值不同的元素才被视为是重复元素。原因是这些元素不会进行竞争。但是,无法添加键相同而值不同的元素,因为无法从逻辑上确定哪个竞争值有效。

  索引器的设计模式

  通常需要定义索引器的两个重载(overloads),一个接受整型数下标的可读写的索引器;一个是接受字符串键值的只读索引器。

public dataproviderelement this[int index]
{
 get
 {
  return (dataproviderelement)baseget(index);
 }
 set
 {
  if(baseget(index) != null)
   baseremoveat(index);

  baseadd(index, value);
 }
}

public new dataproviderelement this[string name]
{
 get
 {
  return (dataproviderelement)baseget(name);
 }
}

  其他事项

  通常还需要公开一些对集合操作的方法,大致如下:

public int indexof(dataproviderelement value)
{
 return baseindexof(value);
}

public void add(dataproviderelement value)
{
 baseadd(value);
}

public void remove(dataproviderelement value)
{
 if(baseindexof(value) >= 0)
  baseremove(value.name);
}

public void remove(string name)
{
 baseremove(name);
}

public void removeat(int index)
{
 baseremoveat(index);
}

public void clear()
{
 baseclear();
}

  总结

  这只是我想要撰写的有关 .net 2.0 中的基础技术篇文章中的第一篇。在这篇文章中,我介绍了有关配置方面的一些基本概念,并阐述了在 .net 2.0 中如何定制你自己的配置处理程序,以及这过程中需要注意的一些细节问题。您已经看到,通过对 .net 2.0 中提供的基础架构的扩展,我们可以很容易完成特性化的配置定制。
发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表