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

  引言

  现代软件开发中,各种技术、技巧越来越依赖配置,譬如客户端对用户体验的个性化设置、系统的各种运行时参数设置、可插拔的插件机制、基于配置的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 中提供的基础架构的扩展,我们可以很容易完成特性化的配置定制。

时间: 2024-10-02 01:45:09

.NET 2.0 中的自定义配置处理的相关文章

ASP.NET 2.0中使用自定义provider

在ASP.NET 2.0中,新增加的membership provider功能,以及结合功能强大 的一系列注册,登陆控件,可以很方便地对用户的登陆和权限等进行管理(参见 <<ASP.NET 2.0登陆控件简介>>). 但是,可能大家会发现,ASP.NET 2.0自带的这些登陆控件和membership的管 理功能,默认的是和sql server 2005 express搭配使用的,那么,如何改成使 用SQL Server 2000或者是其他的数据源,如access,oracle等呢

phpstrom10.0-phpStrom10.0中的环境配置

问题描述 phpStrom10.0中的环境配置 本人新手一枚,现在学习php,公司使用的php ide是phpstrom10.0的,找了很久都没找到可以用的配置方法,不知道是什么原因.想看看论坛里面的高手能不能帮我解决下这个问题.我的目的很简单就是把环境配置好,写一个简单的hello world之类的能在浏览器访问. 解决方案 安装XAMPP 设置好网站根目录,假设设置到D:WWW 然后你直接访问127.0.0.1 就可以 解决方案二: 本地安装一个XAMPP 就可以 具体的可以联系我 phps

在ASP.NET中创建自定义配置节(翻译)

asp.net|创建|asp.net 一.介绍 ASP.NET Web应用程序用一种内置的方法访问简单的"键/值"配置数据.在Web.config文件中,你可以创建节来存储简单的"键/值"对.例如,新建一个ASP.NET项目,在Web.config文件中添加如下的标记作为元素的子标记: 该节包含了用两个标记定义的"键/值"对,你可以通过Page对象内置的ConfigurationSettings属性获得它们的值.作为开始,在你的项目中新建一个名为

在.NET 2.0 中使用自定义事务操作

.net 2.0 framework 中新增了 System.Transactions 命名空间,其中提供的一系列接口和类使得在.net 2.0 中使用事务比起从前要方便了许多.有关在 .net 2.0 下操作数据库事务的文章已经有了很多,这里只提一下如何设计自定义事务操作. 一.事务使用基础 先看一段使用事务的代码: 1using (TransactionScope ts= new TransactionScope())2{3 //自定义操作4 ts.Complete();5} 这里使用 us

在NetBeans 6.0中安装和配置Ruby Support

下载Ruby支持 如果您尚未安装NetBeans 6.0 IDE,请打开NetBeans IDE 6.0下载页面,然后下载一个个含有Ruby支 持的IDE版本. 如果已经有NetBeans IDE 6.0,但是NetBeans IDE 6.0中没有Ruby支持,则按照以下步骤 将Ruby支持添加到该IDE中. 如果您使用代理上网,可在主菜单中选择Tools > Options,并选择Manual Proxy Settings,输入 您的HTTP代理服务器的名称和端口号,然后单击OK按钮. 在主菜

在.NET2.0中使用自定义事务操作_实用技巧

.net 2.0 framework 中新增了 System.Transactions 命名空间,其中提供的一系列接口和类使得在.net 2.0 中使用事务比起从前要方便了许多.有关在 .net 2.0 下操作数据库事务的文章已经有了很多,这里只提一下如何设计自定义事务操作. 一.事务使用基础 先看一段使用事务的代码: 1using (TransactionScope ts= new TransactionScope())2{3 //自定义操作4 ts.Complete();5} 这里使用 us

ASP.NET 2.0 中使用自定义缓存依赖

asp.net|缓存 在 ASP.NET 1.x 中,我们可以使用 CacheDependency 来实现缓存依赖策略,但由于这个类是 sealed 的,我们无法继承这个类来实现我们自己的策略.但是到了 ASP.NET 2.0,我们已经可以从这个类派生出自己的缓存依赖类了. 假定我们要设计一个页面,需要从博客园首页获取最新的贴子信息.为了提高性能,我们希望页面数据仅当博客园首页有更新时才重新生成,否则的话就直接从缓存中获取.如何实现? 一.设计 BlogCacheDependency 类 先分析

在Web.config或App.config中的添加自定义配置

.Net中的System.Configuration命名空间为我们在web.config或者app.config中自定义配置提供了完美的支持.最近看到一些项目中还在自定义xml文件做程序的配置,所以忍不住写一篇用系统自定义配置的随笔了. 如果你已经对自定义配置了如指掌,请忽略这篇文章. 言归正传,我们先来看一个最简单的自定义配置 <?xml version="1.0" encoding="utf-8" ?> <configuration>

在 RHEL/CentOS 7.0 中安装 LAMP

跳过 LAMP 的介绍,因为我认为你们大多数已经知道了.这个教程会集中在如何在升级到 Apache 2.4 的 Red Hat Enterprise Linux 7.0 和 CentOS 7.0 中安装和配置 LAMP:Linux.Apache. MariaDB. PHP/PhpMyAdmin. 在 RHEL/CentOS 7.0 中安装 LAMP 前置要求 根据使用的发行版是 RHEL 还是 CentOS 7.0,按照下面的链接来进行最小化的系统安装,网络使用静态 IP. 对于 RHEL 7.