现在的位置: 首页 > 综合 > 正文

.NET(C#) 配置系统 – 剖析AppSettings实现

2012年05月02日 ⁄ 综合 ⁄ 共 7623字 ⁄ 字号 评论关闭

 

 

返回目录

1. 浏览AppSettings

AppSettings为程序员提供了方便简洁的配置存储,下面是一个典型的AppSettings在应用程序的配置文件:

<?xml version="1.0" encoding="utf-8" ?>

<configuration>

  <appSettings>

    <clear/>

    <add key="key1" value="value1"/>

    <add key="key2" value="value2 A"/>

    <add key="key2" value="value2 B"/>

  </appSettings>

 

</configuration>

<configuration>是.NET配置系统的XML根节点,接着<appSettings>中<clear/>上删除继承下来的映射AppSettings值(如果有的话),通过<add>来添加一对键值,如果两个键相同,如例子中的两个key2,那么之前的键值会被后来的改写,即这个配置文件AppSettings中key2键的值是value2 B.

 

AppSettings是一个ConfigurationSection,那么我们首先可以通过Configuation的GetSection可以浏览其内容,当然Configuration类提供一个AppSettings属性来方便用户,看其源代码,其实就是用GetSection来返回”appSettings”ConfigurationSection;

//Configuration类的AppSettings属性源代码

public AppSettingsSection AppSettings

{

    get

    {

        return (AppSettingsSection)this.GetSection("appSettings");

    }

}

 

那么通过Configuration类浏览AppSettings就是这样的:

(代码1:通过Configuration.GetSection浏览)

Configuration conf = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);

AppSettingsSection appSet = conf.AppSettings;

foreach (KeyValueConfigurationElement ele in appSet.Settings)

    Console.WriteLine("键:{0} 值:{1}", ele.Key, ele.Value);

 

看到这里,有些读者可能发现这个并不是最常用的浏览方法,是的,上述方法是浏览.NET配置文件的最原始方法,优点是灵活可读可写,缺点是有些复杂,对于AppSettings这种方便简洁的配置存储,我们还可以使用另一种更常见的方法,就是用ConfigurationManager类。这个类的AppSettings属性返回一个NameValueCollection(此类在System.Collections.Specialized命名空间内)可以直接供用户查询。

(代码2:通过ConfigurationManager浏览)

System.Collections.Specialized.NameValueCollection nvc =

    ConfigurationManager.AppSettings;

foreach (string key in nvc.AllKeys)

    Console.WriteLine("键:{0} 值:{1}", key, nvc[key]);

 

两种方法输出都一样:

键:key1 值:value1

键:key2 值:value2 B

 

 

返回目录

2. 寻找AppSettings根源

我们说AppSettings本质上是一个ConfigurationSection,接下来是时候探索一下这个ConfigurationSection了,先来看看列举一下本地应用程序配置文件继承下来的所有ConfigurationSection。

这里主要通过Configuration类的Sections和SectionGroups属性来枚举

Configuration conf = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);

Console.WriteLine("本地配置文件: {0}\n", conf.FilePath);

Console.WriteLine("=== 所有SectionGroup");

foreach (ConfigurationSectionGroup grp in conf.SectionGroups)

    Console.WriteLine(grp.SectionGroupName);

 

Console.WriteLine();

Console.WriteLine("=== 所有Section");

 

foreach (ConfigurationSection sec in conf.Sections)

    Console.WriteLine(sec.SectionInformation.SectionName);

输出结果:

本地配置文件: E:\My Documents\Visual Studio 2008\Projects\TTC\TTC\bin\Release\Mg

en.exe.Config

 

=== 所有SectionGroup

system.serviceModel

system.net

system.transactions

system.web

system.runtime.serialization

system.serviceModel.activation

system.xml.serialization

=== 所有Section

system.data

windows

system.webServer

mscorlib

system.data.oledb

system.data.oracleclient

system.data.sqlclient

configProtectedData

satelliteassemblies

system.data.dataset

startup

system.data.odbc

system.diagnostics

runtime

system.codedom

system.runtime.remoting

connectionStrings

assemblyBinding

appSettings

system.windows.forms

AppSettings必须在其中(黄色标注)

 

我们的本地应用程序配置文件里没有定义AppSettings区域,但却可以直接使用,这是就是因为AppSettings是本地应用程序配置文件所继承的配置选项,更准确的说,继承自.NET配置系统中最顶端的配置文件machine.config

 

machine.config的文件路径可以通过如下两种方式得到,结果都是一样的

var path1 = ConfigurationManager.OpenMachineConfiguration().FilePath;

var path2 = new ConfigurationFileMap().MachineConfigFilename;

 

打开machine.config,在开头就可以找到AppSettings的定义(当然还有另外一个常见的ConfigurationSection:ConnectionStrings区域的定义)

<configuration>

  <configSections>

    <section name="appSettings"

             type="System.Configuration.AppSettingsSection, System.Configuration, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"

             restartOnExternalChanges="false"

             requirePermission="false"/>

    <section name="connectionStrings"

             type="System.Configuration.ConnectionStringsSection, System.Configuration, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"

             requirePermission="false"/>

    <section name="mscorlib"

             type="System.Configuration.IgnoreSection, System.Configuration, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"

             allowLocation="false"/>

 

    后续内容省略……

 

好了,看到这里,一切就”真相大白”了,AppSettings其实就是一个普普通通的System.Configuration.AppSettingsSection的配置文件定义!下面我们就开始看看这个AppSettingsSection类!

 

 

 

返回目录

3. 探索AppSettingsSection

AppSettings是微软预定义在.NET Framework里的,但并没有享受什么特殊服务,就是一个由AppSettingsSection代表的普通ConfigurationSection,来看看这个类。

首先别忘了ConfigurationSection继承自ConfigurationElement,后者有Properties属性是ConfigurationPropertyCollection类型,用来添加用户自定义属性(这个属性是配置文件中的节点属性,当然实现起来也使用.NET的普通属性形式)。

AppSettingsSection只有两个属性,File和Settings。File属性大家都知道,MSDN上也有说明,但Settings属性却几乎没见过,是因为Settings属性是默认容器属性(IsDefaultCollection = true),它的ConfigurationProperty名称索性是空字符串。

Settings属性(ConfigurationProperty)是被这样初始化的:

//AppSettingsSection的EnsureStaticPropertyBag方法 部分代码

//此方法会在Properties属性的get中被调用

s_propAppSettings =

    new ConfigurationProperty(

        null,

        typeof(KeyValueConfigurationCollection),

        null,

        ConfigurationPropertyOptions.IsDefaultCollection);

 

这样的话,向AppSettingsSection里直接添加数据,其实就是向AppSettingsSection中Settings这个KeyValueConfigurationCollection添加数据。

 

 

返回目录

4. 探索KeyValueConfigurationCollection

KeyValueConfigurationCollection继承与ConfigurationElementCollection,并且是默认的AddRemoveClearMap类型的容器,这里必须要说一下ConfigurationElementCollection的ThrowOnDuplicate属性。

ConfigurationElementCollection的ThrowOnDuplicate针对AddRemoveClearMap和AddRemoveClearMapAlternate是返回true的。

可以参考其源代码:

protected virtual bool ThrowOnDuplicate

{

    get

    {

        if ((this.CollectionType != ConfigurationElementCollectionType.AddRemoveClearMap) && (this.CollectionType != ConfigurationElementCollectionType.AddRemoveClearMapAlternate))

        {

            return false;

        }

        return true;

    }

}

 

但KeyValueConfigurationCollection将其改写为false,这个非常重要,这样AppSettings中的键值可以重复添加并且如果有相同键的话,值会被覆盖!

 

另外,由于ConfigurationElementCollection的容器操作函数都是只针对继承类可见的,因此KeyValueConfigurationCollection还定义了Add,Remove,Clear公有函数,这三个函数使用ConfigurationElementCollection内部继承类可见函数BaseAdd, BaseRemove, BaseClear。用户可以使用这三个函数对AppSettings进行动态修改,这也是上面讲到过的,只有通过Configuration类才可以做到,而ConfigurationManager的AppSettings是只读的。

image

 

比如将文章最上方的AppSettings中的key1键移除并保存config文件:

Configuration conf = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);

AppSettingsSection appsetSection = conf.AppSettings;

 

KeyValueConfigurationCollection collec = appsetSection.Settings;

collec.Remove("key1");

conf.Save();

 

 

另外KeyValueConfigurationCollection也没有改写ConfigurationElementCollection的Add/Remove/ClearElementName,三者保持默认值,即:add, remove, clear

 

最后KeyValueConfigurationCollection存储KeyValueConfigurationElement,这个定义在ConfigurationElementCollectionAttribute特性中

[ConfigurationCollection(typeof(KeyValueConfigurationElement))]

public class KeyValueConfigurationCollection : ConfigurationElementCollection

这个KeyValueConfigurationElement也是普通的ConfigurationElement,这里定义两个数据属性,Key和Value分别代表键和值。

 

返回目录

总述

最后,让我们再来看看文章开头的那个AppSettings,此时你应该对AppSettings有了全新的认识,我把原理加成注释:

<?xml version="1.0" encoding="utf-8" ?>

<!-- 这是.NET配置文件根节点 -->

<configuration>

  <!-- machine.config中定义这个appSettings区域 -->

  <!-- 代表AppSettingsSection这个ConfigurationSection -->

  <appSettings>

    <!-- 这里设置AppSettingsSection中的Settings属性 -->

    <!-- 由于Settings属性具有默认容器属性,因此不需要指定名称 -->

    <!-- 操作KeyValueConfigurationCollection(Settings属性代表的类型) -->

 

    <!-- 这是ConfigurationElementCollection中的ClearElementName -->

    <!-- KeyValueConfigurationCollection没有改写此值,保留默认 -->

    <!-- 删除一切可能继承来的appSettings值 -->

    <clear/>

 

    <!-- 在KeyValueConfigurationCollection中添加KeyValueConfigurationElement -->

    <!-- add是ConfigurationElementCollection的AddElementName的默认值 -->

    <!-- key和value是KeyValueConfigurationElement的属性 -->

    <add key="key1" value="value1"/>

 

    <!-- 同上 -->

    <add key="key2" value="value2 A"/>

 

    <!-- 由于KeyValueConfigurationCollection改写ThrowOnDuplicate为false -->

    <!-- 因此相同键会被改写,此时appSettings中key2键的值被改写成value2 B -->

    <add key="key2" value="value2 B"/>

  </appSettings>

</configuration>

抱歉!评论已关闭.