ConfigurationSettings类解析

原创 2007年10月13日 09:01:00

 

 .Net Framework 源码分析

 

.Net Framework的源码是微软编程大师们智慧的结晶,是我们开发人员梦寐以求的知识宝藏。

挖掘这座宝藏是我们快速提升自身编程思想水平的重要途径。

下面是我研究分析 .Net Framework一部分代码后的一点心得,共享出来,希望对大家有所帮助,当然,分析不对的地方,还望指正,不胜感激。

System.Configuration.ConfigurationSettings

相信大家对这个类都不陌生吧。 ConfigurationSettings类重要的方法是(在我下面的分析中,方法也包括属性)

  • AppSettings属性 用于获取 元素配置节中的配置设置。
  • GetConfig方法 返回用户定义的配置节的配置设置。

在我们的项目开发中,我们经常通过 ConfigurationSettings.AppSettings["myKey"] 的方法 来获取 web.config 配置项上 appSettings 的配置值。调用这个ConfigurationSettings.AppSettings["myKey"]索引器我们就可以获取到 web.cofing 配置项 appSettings 的配置值,这太方便了。如果要我们设计一个这样的功能的时候,我们会有什么想法呢。 我的想法大概的是这样的:

1.        加载 web.config 配置文件的内容

2.        分析 web.config 配置文件配置项 appSettings 节点的内容,并加载到配置项管理类中。

3.        配置项管理类中应该有一个索引器,方便外部系统访问。

让我们来分析大师们是如何实现这个类的。看看大师级人物的代码和设计思路有何高明之处。

//ConfigurationSettings类的定义

public sealed class ConfigurationSettings

{

}

C# 关键字 sealed 表明此类是不能被继承的。

//静态构造函数

static ConfigurationSettings()

{

   _initState = InitState.NotStarted;

   _initLock = new object();

}

一个类最先运行的代码段就是静态构造函数,并且对于整个程序域而言静态构造函数只运行一次。
C#
关键字 static 加上类名称的方法函数就是静态构造函数。
对于一个类来说,只能有一个静态构造函数。
静态构造函数的作用主要是初始化静态变量。
C#关键字 static 约束的类方法里面的代码都只能调用 静态变量或者静态方法,静态属性等。

静态方法:C#关键字 static 约束的方法就是静态方法(有些教材可能会称为类方法),里面的代码都只能调用 静态变量或者静态方法,静态属性等。

//静态变量的定义代码

private static object _initLock;

C#关键字 static 表明此变量为静态变量。

//构造函数

private ConfigurationSettings()

{

}

发现上面的构造函数跟我们平时所写的类的构造函数有什么不同吗?
对了,就是访问权限的约束关键字 private ,平时构造函数的约束关键字都是 public
那么将构造函数访问权限设置为 private有什么目的呢?

1.        防止别人的代码通过 new 操作生成对象实例。

如:System.Configuration.ConfigurationSettings config = new System.Configuration.ConfigurationSettings();

你会发现上面的代码编译不通过,原因就是访问了 private 的构造函数,当然编译不通过啦!

2.        保证一个类仅有一个实例。

这里就是设计模式中的 Singleton 单件模式了,设置构造函数的访问权限为 private 是实现 Singleton 模式的前提

 //AppSettings静态只读属性

    public static NameValueCollection AppSettings

    {

       get

       {

           ReadOnlyNameValueCollection config = (ReadOnlyNameValueCollection) GetConfig("appSettings");

           if (config == null)

           {

               config = new ReadOnlyNameValueCollection(new

                 CaseInsensitiveHashCodeProvider(CultureInfo.InvariantCulture), new   CaseInsensitiveComparer(CultureInfo.InvariantCulture));

          config.SetReadOnly();

            }

        return config;

        }

     }

 

通过上面的代码我们可以知道,此属性为静态只读属性(static 关键字,只有 get 操作,而没有 set 操作)
因为 NameValueCollection 类定义了索引访问器,所以平时我们的代码都是这样写的 ConfigurationSettings.AppSettings["myKey"]
,对于["myKey"]这种使用 []号访问的索引器,我们下面分析NameValueCollection类时再说明索引器。

ReadOnlyNameValueCollection config = (ReadOnlyNameValueCollection) GetConfig("appSettings");
注意到参数的值是 appSettings 了吗?
是不是跟我们 web.config 里面的 appSettings 的配置节点项有关联呢?他们有什么关系吗?我们往下看。
这段代码调用了ConfigurationSettings类的另外一个静态方法,代码如下:

public static object GetConfig(string sectionName) //当然这时 sectionName == "appSettings"

{

   if ((sectionName == null) || (sectionName.Length == 0))

   //判断 string 的值是不是为Empty时,应该用 sectionName.Length == 0 来判断

   {

      return null;

   }

   if (_initState < InitState.Usable)

   {

      EnsureConfigurationSystem();

   }

   if (_initError != null)

   {

      throw _initError;

   }

   return _configSystem.GetConfig(sectionName);

}



代码段:

if (_initState < InitState.Usable)

{

   EnsureConfigurationSystem();

}

InitState 只是一个私有的枚举类型 enum

private enum InitState

{

   NotStarted,

   Started,

   Usable,

   Completed

}

刚才ConfigurationSettings类的静态构造函数是设置
initState = InitState.NotStarted;
那么第一次运行时,肯定会执行 EnsureConfigurationSystem()方法了,我们接着看看代码的实现

private static void EnsureConfigurationSystem()

{

   lock (_initLock)

   {

      if (_initState < InitState.Usable)

      {

         _initState = InitState.Started;

         try

         {

            _configSystem = new DefaultConfigurationSystem();

            _initState = InitState.Usable;  

         }

         catch (Exception exception)

         {

            _initError = exception;

            _initState = InitState.Completed;

            throw;

         } 

      }

   }

}

C#关键字 lock 加锁处理。
lock 确保当一个线程位于代码的临界区时,另一个线程不进入临界区。如果其他线程试图进入一个锁定代码,则它将在释放该对象前一直等待(块)。
MSDN的解释:lock 关键字可将语句块标记为临界区,方法是获取给定对象的互斥锁,执行语句,然后释放该锁。
通常,如果要保护实例变量,则 lock(this);如果要保护 static 变量(或者如果临界区出现在给定类的静态方法中),则 lock(typeOf (class))

 

  2007-7-24 13:57:32

 

 

  陈英豪

  
  
  
等级:版主
  
文章:17
  
积分:19
  
注册:2007-7-2

       

2

 

 

 

 

_configSystem = new DefaultConfigurationSystem();

private static IConfigurationSystem _configSystem;

_configSystem 是一个接口变量。
先看看接口 IConfigurationSystem 定义

public interface IConfigurationSystem

{

    // Methods

    object GetConfig(string configKey);

    void Init();

}

接着我们跟踪实现了 IConfigurationSystem 接口的 DefaultConfigurationSystem 类看看
类的定义:

internal class DefaultConfigurationSystem : IConfigurationSystem

{

}

C# 关键字 internal 表明此类只能被当前的 dll 里面的类使用。
顺便提一提 protected internal 这样的二个关键字的约束。它表明这个只能被当前 dll 里面的类使用或者不是当前 dll 里面的子类使用,记得是 或者 的关系
我们还是先从这个类的构造函数分析开始:

internal DefaultConfigurationSystem()

{

}

这里的构造函数使用 internal,并不是像 ConfigurationSettings 类构造函数的 private
它的访问权限比 ConfigurationSettings 的类的松一点,允许当前 dll 里面的类可以通过 new 操作来生成多个 DefaultConfigurationSystem 实例。
所以这里才有上面的代码:
_configSystem = new DefaultConfigurationSystem();
的代码调用。
重要方法GetConfig的部分关键代码内容:

object IConfigurationSystem.GetConfig(string configKey) //当然这里还是 configKey == "appSettings"

{

   if (!this._isAppConfigInited)

   {

      this.EnsureInit(configKey);

   }

   ConfigurationRecord record = null;

   if (record != null)

   {

     return record.GetConfig(configKey);

   }

   return null;

}

接下来我们就要分析 EnsureInit 方法。

private void EnsureInit(string configKey)

{

  try

  {

     ConfigurationRecord record = new ConfigurationRecord();

     bool flag2 = record.Load(this._machineFilename);

     //加载配置文件信息,这里是加载 machine.config 的信息,并不是 web.config的信息

     this._machineConfig = record;

     ///....省略

  }

  catch (Exception exception)

  {

     this._initError = exception;

     ConfigurationSettings.SetInitError(this._initError);

     this._isMachineConfigInited = true;

     this._isAppConfigInited = true;

     throw;

  }

  finally

  {

     lock (this)

     {

        ConfigurationSettings.CompleteConfigInit();

        Monitor.PulseAll(this);

     }

  }

}

这里只是加载 .Net Framework machine.config 全局配置信息,在这个配置信息项中,你可以找到这样的配置项。

<configuration>

 <configSections>

  <section name="appSettings" type="System.Configuration.NameValueFileSectionHandler, System"/>

 </configSections>

<configuration>

这个配置项是什么意思呢? name="appSettings"这里表示自定义的配置项键。也就是 appSettings
type="System.Configuration.NameValueFileSectionHandler, System"
表示 appSettings 这个自定义配置键的处理类是 System.Configuration.NameValueFileSectionHandler,后面的 System表示此类所在的 dll的名称。
System.Configuration.NameValueFileSectionHandler
这个类必须实现 IConfigurationSectionHandler 这个接口。下面的代码就会调用这个接口里面的方法来获取配置数据。
通过调用 ConfigurationRecord.GetConfig -->> ConfigurationRecord.ResolveConfig -->> ConfigurationRecord.Evaluate

private object Evaluate(string configKey) // 当然这里的值还是 appSettings

{

    IConfigurationSectionHandler factory = this.GetFactory(configKey); //工厂模式

    object config = (this._parent != null) ? this._parent.GetConfig(configKey) : null;

    string[] keys = configKey.Split(new char[] { '/' });

    XmlTextReader reader = null;

    object obj3 = null;

    try

    {

        reader = this.OpenXmlTextReader(this._filename);  //加载我们的 web.config 文件

        obj3 = this.EvaluateRecursive(factory, config, keys, 0, reader);  //调用自定义配置项处理类的接口方法获取数据。下面分析。

    }

    catch (ConfigurationException) //catch 的用法,先获取自定义的异常类,这才是好方法。

    {

        throw;

    }

    catch (Exception exception)

    {

        throw this.TranslateXmlParseOrEvaluateErrors(exception);

    }

    finally

    {

        if (reader != null)

        {

            reader.Close();

        }

    }

    //省略.....

    return obj3;

}

接下来的 GetFactory 的方法就是关键啦,为我们以后写自定义配置项类做参考了。

private IConfigurationSectionHandler GetFactory(string configKey)

{

    if ((this._factories != null) && this._factories.Contains(configKey))

    {

        object obj2 = this._factories[configKey];

        if (obj2 == RemovedFactorySingleton)

        {

            return null;

        }

        IConfigurationSectionHandler handler = obj2 as IConfigurationSectionHandler;

        //as 运算符用于执行可兼容类型之间的转换。

        //as 运算符类似于类型转换,所不同的是,当转换失败时,as 运算符将产生空,而不是引发异常。

        if (handler == null)

        {

            string typeName = (string) obj2;

            obj2 = null;

            new ReflectionPermission(PermissionState.Unrestricted).Assert();

            try

            {

                Type c = Type.GetType(typeName);

                if (c != null)

                {

                    if (!typeof(IConfigurationSectionHandler).IsAssignableFrom(c))

                    {

                        throw new ConfigurationException(SR.GetString("Type_doesnt_implement_IConfigSectionHandler", new object[] { typeName }));

                    }

                    obj2 = Activator.CreateInstance(c, BindingFlags.CreateInstance

                                | BindingFlags.NonPublic | BindingFlags.Public

                                | BindingFlags.Instance, null, null, null);

                    //Activator 就是 Net Framework 著名的反射机制的实现类啦。

                                      //Activator.CreateInstance可以动态创建某种类型的实例对象。

                    在这里,它创建了什么对象了,也就是我们上面分析的 System.Configuration.NameValueFileSectionHandler 类对象啦

                }

            }

            catch (Exception exception)

            {

                throw new ConfigurationException(exception.Message, exception);

            }

            finally

            {

                CodeAccessPermission.RevertAssert();

            }

            //省略

        }

        return handler;

    }

    if (!this._factoriesNoInherit && (this._parent != null))

    {

        return this._parent.GetFactory(configKey);

    }

    return null;

}

 

  2007-7-24 14:12:40

 

 

  陈英豪

  
  
  
等级:版主
  
文章:17
  
积分:19
  
注册:2007-7-2

       

3

 

 

 

 

接下来的 EvaluateRecursive 方法将会调用 System.Configuration.NameValueFileSectionHandler 类的接口方法 Create

private object EvaluateRecursive(IConfigurationSectionHandler factory, object config, string[] keys, int iKey, XmlTextReader reader)

{

    string text = keys[iKey];

    int depth = reader.Depth;

    do

    {

        if (!reader.Read())

        {

            break;

        }

    }

    while (reader.NodeType != XmlNodeType.Element);

    while (reader.Depth == (depth + 1))

    {

        if (reader.Name == text)

        {

            if (iKey < (keys.Length - 1))

            {

                config = this.EvaluateRecursive(factory, config, keys, iKey + 1, reader);

                continue;

            }

            CreatePermissionSetFromLocation(this._filename).PermitOnly();

            try

            {

                int line = reader.LineNumber;

                try

                {

                    ConfigXmlDocument document = new ConfigXmlDocument();

                    document.LoadSingleElement(this._filename, reader);

                    config = factory.Create(config, null, document.DocumentElement);

     //调用 System.Configuration.NameValueFileSectionHandler 类的方法了,

                    下面要进入 System.Configuration.NameValueFileSectionHandler 类的分析了。

                }

                catch (ConfigurationException)

                {

                    throw;

                }

                catch (XmlException)

                {

                    throw;

                }

                catch (Exception exception)

                {

                    throw new ConfigurationException(SR.GetString("Exception_in_config_section_handler"), exception, this._filename, line);

                }

                continue;

            }

            finally

            {

                CodeAccessPermission.RevertPermitOnly();

            }

        }

        this.StrictSkipToNextElement(reader);

    }

    return config;

}

IConfigurationSectionHandler 接口成员
由所有配置节处理程序实现,以分析配置节的 XML。返回的对象被添加到配置集合中,并通过 GetConfig 访问。

object Create(

   object parent,

   object configContext,

   XmlNode section

);

IConfigurationSectionHandler 接口是我们写自定义配置节点项时必须要实现的接口。下面再说。
NameValueFileSectionHandler
类的定义

public class NameValueFileSectionHandler : IConfigurationSectionHandler

{

    // Methods

    public NameValueFileSectionHandler();

    public object Create(object parent, object configContext, XmlNode section);

}

NameValueFileSectionHandler.Create 方法的实现非常简单,就是获取 参数中 XmlNode section 的数据,通过 key value 对应的关系 保存到 私有的 HashTable类型的变量中 section 的参数值大概是这样的

 <appSettings>

     <add key="ConnectionString" value="xxxxxxxxxx" />

        <add key="__SystemID__" value="xxxxxxxxxx" />

    </appSettings>

分析了 System.Configuration.ConfigurationSettings 后,来看看如何实现我们自定义的配置节点管理。
下面的示例代码我采用 VS.NET 的企业代码示例 Duwamish 项目来说明。

第一步,我们要自定义配置节点,web.config 配置节点示例如下:

<configuration>

  <configSections>

    <section name="DuwamishConfiguration" type="Duwamish7.Common.DuwamishConfiguration, Duwamish7.Common" />

  </configSections>

</configuration>




<DuwamishConfiguration> //注意这里就是自定义配置项节点了

    <add key="Duwamish.DataAccess.ConnectionString" value="server=.;User ID=sa;Password=password;database=Duwamish;Connection Reset=FALSE" />

    <add key="Duwamish.Web.EnablePageCache" value="True" />

    <add key="Duwamish.Web.PageCacheExpiresInSeconds" value="3600" />

    <add key="Duwamish.Web.EnableSsl" value="False" />

</DuwamishConfiguration>

第二步,让我们的自定义配置节点处理类实现 IConfigurationSectionHandler 接口

在这里是 dll名称为 Duwamish7.Common 的 Duwamish7.Common.DuwamishConfiguration的类要实现这个接口代码:

public Object Create(Object parent, object configContext, XmlNode section)

{   

    NameValueCollection settings;   

    try

    {

        NameValueSectionHandler baseHandler = new NameValueSectionHandler();

        settings = (NameValueCollection)baseHandler.Create(parent, configContext, section);

    }

    catch

    {

        settings = null;

    }   

    if ( settings == null )

    {

        dbConnectionString        = DATAACCESS_CONNECTIONSTRING_DEFAULT;

        pageCacheExpiresInSeconds = WEB_PAGECACHEEXPIRESINSECONDS_DEFAULT;

        enablePageCache           = WEB_ENABLEPAGECACHE_DEFAULT;

        enableSsl                 = WEB_ENABLESSL_DEFAULT;

    }

    else

    {

        dbConnectionString        = ApplicationConfiguration.ReadSetting(settings, DATAACCESS_CONNECTIONSTRING, DATAACCESS_CONNECTIONSTRING_DEFAULT);

        pageCacheExpiresInSeconds = ApplicationConfiguration.ReadSetting(settings, WEB_PAGECACHEEXPIRESINSECONDS, WEB_PAGECACHEEXPIRESINSECONDS_DEFAULT);

        enablePageCache           = ApplicationConfiguration.ReadSetting(settings, WEB_ENABLEPAGECACHE, WEB_ENABLEPAGECACHE_DEFAULT);

        enableSsl                 = ApplicationConfiguration.ReadSetting(settings, WEB_ENABLESSL, WEB_ENABLESSL_DEFAULT);

    }   

    return settings;

}

第三步,如何启动我们的自定义配置节点处理类

System.Configuration.ConfigurationSettings.GetConfig("DuwamishConfiguration");

到此,我们就搞掂了自定义配置节点管理了。自定义配置节点管理可以避免将很多的配置信息都放到 appSettings 节点中,便于管理。通过类来管理配置信息,可以在多个项目中重用,我们也可以写一个基类,来获取统一的配置项键管理。如在我们的项目中差不多每个 web.config 都会有

 <add key="__SystemID__" value="xxxx" />

 <add key="__SystemName__" value="xxxx" />

 <add key="__SystemPassword__" value="xxxx" />

如果我们的基类提供了获取这三个对应 key 值的配置项,其它的项目需要扩展时,只须继承我们的配置节点管理基类就OK了。

 

  2007-7-24 14:16:26

 

 

  陈英豪

  
  
  
等级:版主
  
文章:17
  
积分:19
  
注册:2007-7-2

       

4

 

 

 

 

好了,ConfigurationSettings 类我就分析到这里吧!

谢谢!

 

 

关于ASP.NET C#配置Web.config的方法及读取方法

关于ASP.NET C# 配置Web.config的方法以及巧用设置   2010-09-03 09:41:33|  分类: asp.net技术 |  标签: |举报 |字号大中小 订阅 ...
  • LHW_00002
  • LHW_00002
  • 2014年09月14日 16:39
  • 1833

[Android开发] Json解析工具类,一个类搞定Json的解析

一、简介利用递归的方式反射解析到bean里面二、详细代码1、 Json格式例如服务器指定规定json格式为:{ "code": "……" , // 返回代号,预留字段,默认返回nul...
  • niubitianping
  • niubitianping
  • 2016年11月10日 17:30
  • 3517

java读取XML文件通用工具类(递归调用)

java读取XML文件通用工具类(递归调用) 源代码下载地址:http://www.zuidaima.com/share/1550463285480448.htm...
  • yaerfeng
  • yaerfeng
  • 2014年08月02日 11:45
  • 8939

C#Form下ConfigurationSettings与ConfigurationManager区别

C#Form下ConfigurationSettings与ConfigurationManager  ConfigurationSettings.AppSettings与Configur...
  • poolatu
  • poolatu
  • 2013年03月21日 09:40
  • 4715

"ConfigurationSettings已过时"全面解决方案

难度:3 前期准备:对app.config, web.config文件中配置数据库连接有一定了解。 结论 VS2003:string connStr=System.Configuration...
  • ldy592
  • ldy592
  • 2013年12月31日 15:26
  • 823

php的模板解析类

  • 2017年08月11日 10:48
  • 3KB
  • 下载

二维码工具类,提供多种生成二维码、解析二维码的方法,包括中间logo的二维码等方法

  • 2017年08月01日 17:06
  • 23KB
  • 下载

PHP解析 Simple HTML DOM Parser类

  • 2014年12月05日 11:30
  • 54KB
  • 下载

XML文件解析类

  • 2015年04月24日 15:17
  • 131KB
  • 下载

HttpServletRequest解析设备类型和浏览器类型

  • 2018年01月04日 15:19
  • 2KB
  • 下载
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:ConfigurationSettings类解析
举报原因:
原因补充:

(最多只允许输入30个字)