Coding with .NET

Live with passion, coding with passion....

首页 新随笔 联系 订阅 管理

在 .net 1.0/1.1 中,.net framework给我们提供了基于 xml 文件的配置系统,使我们能够非常方便的读取和扩展配置数据。在 .net 2.0 中,这种基于 xml 文件的配置系统被进一步改善了,更容易被读取和扩展,而且增加了在运行时更改配置和加密配置数据的支持。

如何配置应用程序

在 .net 配置系统中一般包括两种类型的元素:

  • Section: 配置节。
  • Section Group: 配置节组,用于将多个配置节(section)或配置节组(section group)组合起来。在 .net 1.0/1.1 中, section group只有一个名称的属性。而在 Whidbey 中,section group也有类型(从 ConfigurationSectionGroup 类继承)。

在配置文件中使用自定义的配置节/配置节组时,需要首先在configSections元素中申明。如下的一个例子(注意 configuration 元素的 xmlns 属性。下面的配置代码仅仅是为了举例,使用了 machine.config 中定义的配置节、配置节组。):

<configuration xmlns="http://schemas.microsoft.com/.NetConfiguration/v2.0">
  <!-- Define custom configuration sections/section groups. -->
  <configSections>
    <section name="appSettings" type="type here" restartOnExternalChanges="false" />
    <sectionGroup name="system.web" type="type here">
      <section name="pages" type="type here" />
    </sectionGroup>
  </configSections>
  
  <!-- Configuration data -->
  <appSettings>
    <add key="--" value="---" />
  </appSettings>
  
  <system.web>
    <pages>
      <controls>
        <add tagPrefix="chenglin" namespace="MyNamespace.WebControls" assembly="MyAssembly"/>
      </controls>
    </pages>
  </system.web>
</configuration>

.net framework 配置系统在加载配置数据的时候,会搜索一个由 xml 配置文件构成的树型结构来获取配置数据。在 exe 程序中,一般会搜索两个配置文件:

  • machine.config
  • [app name].exe.config

而在一个 web 应用程序中,可能会包括多个配置文件:

  • machine.config
  • web.config (in app v-root)
  • 进入子目录的web.config,并重复2,直到当前请求页面所在的目录。

如何在应用程序中读取配置数据

在 .net 2.0 中,配置数据可以从 System.Configuration.Configuration 类中获取。要获取配置节和配置节组的数据可以访问 Configuration 类的 GetSection(string sectionName), GetSectionGroup(string sectionGroupName) 等方法。比如上述的例子中,我们可以调用 Configuration.GetSection 方法,参数 sectionName 分别为

  • appSettings
  • system.web/pages
来获取上述例子中两个不同配置节的配置数据;访问 Configuration.GetSectionGroup 方法,参数指定为 system.web,获取配置组的配置数据。

要获取 Configuration 类的实例,可以使用 ConfiguartionManager 或者 WebConfigurationManager 类的相应方法。当然也可以使用 ConfigurationSettings 类的 GetConfig 方法,但是它已经被标记为 Obsolete 。 使用 ConfigurationManager 类或者 WebConfigurationManager 类的另外一个好处是我们指定访问某个配置文件,而不仅仅是默认的配置文件。比如在一个 web 应用程序中,当访问应用程序根目录下的页面的时候,在.net framework 1.0/1.1中我们就不是很容易获取子目录下配置文件的配置内容,但在 2.0 中我们可以指定路径来直接访问。

Configuartion Model 与配置文件的映射

一般来说,我觉得为一个应用程序写的配置部分的代码按如下的步骤比较好:首先,我们要知道应用程序到底需要什么配置;然后用 xml 的形式写出来;最后再根据 xml 代码写出所需的配置代码。当然将后面两步的顺序交换也是不错的选择。

在 .net framework 1.0/1.1 中,配置节的处理程序需要实现 IConfigurationSectionHandler 接口,然后在程序中处理 xml 数据。而在 .net framework 2.0 中则不一定需要实现 IConfigurationSectionHandler 接口,只需从相应的类继承就可以,而且可以省掉繁琐的 xml 处理过程。.net 2.0中的几个主要的类包括:

  • ConfigurationElement,独立的配置元素,在xml配置文件中表示配置节的子节点。
  • ConfigurationElementCollection,表示一组配置元素。
  • ConfigurationSection,在配置文件中表示一个配置节。
  • ConfigurationSectionGroup,表示配置节组,可以包含多个配置节或者配置节组。
比如下面的图表列出了在上述的例子中是如何将不同的元素映射到相应的配置类的:
<appSettings>
  <add key="--" value="---" />
</appSettings>
<system.web>
  <pages>
    <controls>
      <add tagPrefix="chenglin" namespace="MyNamespace.WebControls" assembly="MyAssembly"/>
</controls> </pages> </system.web>
Configuration Section
Configuration Element
   
Configuration Section Group
Configuration Section
Configuration Element Collection
Configuration Element
     

创建配置系统所需的代码

看起来比 .net framework 1.0/1.1 中的复杂吧?实际上非常简单的。下面列出了上述的部分代码(如果需要查看完整的代码,可以用 .Net Reflector 查看):

SystemWebSectionGroup class

1 public sealed class SystemWebSectionGroup : ConfigurationSectionGroup
2 {
3   [ConfigurationProperty("pages")]
4   public PagesSection Pages
5   {
6     getreturn (PagesSection)base.Sections["pages"]; }
7   }
8 }

PagesSection class

 1 public sealed class PagesSection : ConfigurationSection
 2 {
 3   static PagesSection()
 4   {
 5      propControls = new ConfigurationProperty("controls"typeof(TagPrefixCollection), null, ConfigurationPropertyOptions.None);
 6      
 7      properties = new ConfigurationPropertyCollection();
 8      properties.Add( propControls );
 9   }
10   
11   [ConfigurationProperty("controls")]
12   public TagPrefixCollection Controls
13   {
14     get{  return (TagPrefixCollection) base[propControls]; }
15   }
16   
17   protected override ConfigurationPropertyCollection Properties
18   {
19     get{  return PagesSection.properties;  }
20   }
21   
22   private static readonly ConfigurationProperty propControls;
23   private static ConfigurationPropertyCollection properties;
24 }
25 

在 PagesSection 类中最重要的代码在static构造函数中。在此构造函数中,我们需要创建 System.Configuration.ConfigurationProperty 类的实例,在这些实例中定义了配置文件中配置数据的属性。这些 ConfigurationProperty 类的实例被添加到一个 System.Configuration.ConfigurationPropertyCollection 对象中。

.net framework 可以通过一个 protected 的 Properties 属性来获取这些已定义的配置数据的信息,用来将对象序列化成相应的 xml 内容,或者将 xml 配置文件中的配置数据转化成相应的对象。这里比较有意思的是,如果在 ConfigurationProperty 中设置的是 ConfigurationPropertyCollection 的类型,则.net framework会自动将其映射到相应的子元素中。比如在前面的例子配置文件中的 pages/controls 元素。

TagPrefixCollection class

 1 [ConfigurationCollection(typeof(TagPrefixInfo), AddItemName="add")]
 2 public sealed class TagPrefixCollection : ConfigurationElementCollection
 3 {
 4   static TagPrefixCollection()
 5   {
 6     _propControls = new ConfigurationProperty(nulltypeof(TagPrefixCollection), null, ConfigurationPropertyOptions.DefaultCollection);
 7     
 8     _properties = new ConfigurationPropertyCollection();
 9     _properties.Add(TagPrefixCollection._propControls);
10   }
11   
12   protected override ConfigurationElement CreateNewElement()
13   {
14     return new TagPrefixInfo();
15   }
16   
17   protected override object GetElementKey(ConfigurationElement element)
18   {
19     TagPrefixInfo info = (TagPrefixInfo) element;
20     if (info.TagName == null) {
21       return string.Concat( info.TagPrefix, ":", info.Namespace, ":", ((info.Assembly == null? string.Empty : info.Assembly) );
22     }
23     else {
24       return (info.TagPrefix + ":" + info.TagName);
25     }
26   }
27   
28   protected override ConfigurationElementCollectionType CollectionType
29   {
30     get{  return ConfigurationElementCollectionType.BasicMap; }
31   }
32   
33   protected override string ElementName
34   {
35     get{  return "add"; }
36   }
37   
38   protected override ConfigurationPropertyCollection Properties
39   {
40     get{  return TagPrefixCollection._properties; }
41   }
42   
43   private static readonly ConfigurationProperty _propControls;
44   private static ConfigurationPropertyCollection _properties;
45 }
46 

上面的代码和 PagesSection 有很多地方都是相同的。不过需要注意的是,因为这是一个collection,所以允许我们设置集合的属性。有两个地方需要设置:

  • TagPrefixInfoCollection.CollectionType,设置集合的类型,可选项包括 AddRemoveClearMap, AddRemoveClearMapAlternate, BasicMap, BasicMapAlternate。
    • AddRemoveClearMap:
      The default type of ConfigurationElementCollection. Collections of this type contain elements that can be merged across a hierarchy of configuration files. At any particular level within such a hierarchy, add, remove, and clear directives are used to modify any inherited properties and specify new ones.
    • AddRemoveClearMapAlternate:
      Same as AddRemoveClearMap, except that this type causes the ConfigurationElementCollection object to sort its contents such that inherited elements are listed last.
    • BasicMap:
      Collections of this type contain elements that apply to the level at which they are specified, and to all child levels. A child level cannot modify the properties specified by a parent element of this type.
    • BasicMapAlternate:
      Same as BasicMap, except that this type causes the ConfigurationElementCollection object to sort its contents such that inherited elements are listed last.
  • TagPrefixInfoCollection.ElementName 和类的 ConfigurationCollectionAttribute 特性,指定子元素的名称。默认使用 add 。

另外在 TagPrefixInfoCollection 类中很重要的方式有 CreateNewElement 和 GetElementKey,它们被 .net framework 用来创建配置元素的对象,以及被用来确定配置元素在集合中的键值。

TagPrefixInfo class

最后看看 TagPrefixInfo 类。

 1 public sealed class TagPrefixInfo : ConfigurationElement
 2 {
 3   static TagPrefixInfo()
 4   {
 5     s_elemProperty = new ConfigurationElementProperty(new CallbackValidator(typeof(TagPrefixInfo), new ValidatorCallback(Validate)));
 6     _propTagPrefix = new ConfigurationProperty("tagPrefix"typeof(string), "/"null, StdValidatorsAndConverters.NonEmptyStringValidator, ConfigurationPropertyOptions.Required);
 7     _propNamespace = new ConfigurationProperty("namespace"typeof(string), nullnull, StdValidatorsAndConverters.NonEmptyStringValidator, ConfigurationPropertyOptions.None);
 8     _propAssembly = new ConfigurationProperty("assembly"typeof(string), nullnull, StdValidatorsAndConverters.NonEmptyStringValidator, ConfigurationPropertyOptions.None);
 9     
10     _properties = new ConfigurationPropertyCollection();
11     _properties.Add(_propTagPrefix);
12     _properties.Add(_propNamespace);
13     _properties.Add(_propAssembly);
14   }
15   
16   private static void Validate(object value)
17   {
18     // validation code here
19   }
20   
21   [StringValidator(MinLength=1), ConfigurationProperty("assembly")]
22   public string Assembly
23   {
24     get{  return (stringbase[TagPrefixInfo._propAssembly];  }
25     set{  base[TagPrefixInfo._propAssembly] = value;  }
26   }
27   
28   [ConfigurationProperty("namespace"), StringValidator(MinLength=1)]
29   public string Namespace
30   {
31     get{  return (stringbase[TagPrefixInfo._propNamespace]; }
32     set{  base[TagPrefixInfo._propNamespace] = value; }
33   }
34   
35   [StringValidator(MinLength=1), ConfigurationProperty("tagPrefix", RequiredValue=true, DefaultValue="/")]
36   public string TagPrefix
37   {
38     get{  return (stringbase[TagPrefixInfo._propTagPrefix]; }
39     set{  base[TagPrefixInfo._propTagPrefix] = value; }
40   }
41   
42   protected override ConfigurationPropertyCollection Properties
43   {
44     get{  return TagPrefixInfo._properties; }
45   }
46   
47   protected override ConfigurationElementProperty ElementProperty
48   {
49     get{  return TagPrefixInfo.s_elemProperty;  }
50   }
51   
52   private static readonly ConfigurationProperty _propAssembly;
53   private static ConfigurationPropertyCollection _properties;
54   private static readonly ConfigurationProperty _propNamespace;
55   private static readonly ConfigurationProperty _propTagPrefix;
56   private static readonly ConfigurationElementProperty s_elemProperty;
57 }
58 

TagPrefixInfo 类中重载了 ElementProperty 属性,指定如何验证配置数据。因此,对于一个 ConfigurationElement 来说,除了可以使用 ConfigurationProperty 指定元素属性的信息,也可以使用 ConfigurationElementProperty 指定元素的信息,做到配置元素局部和整体的验证。

在创建好上面所以的代码之后,要读取或者创建配置文件都很容易,这里就不介绍了。 :)

posted on 2005-07-27 16:55  Lin  阅读(3536)  评论(7编辑  收藏  举报