破解.NET 2.0配置之谜(二)-阿里云开发者社区

开发者社区> 大数据> 正文
登录阅读全文

破解.NET 2.0配置之谜(二)

简介:

如果您是.NET 2.0配置的新手,或尚未掌握类型验证和转换的概念,您应该首先阅读以前的文章,可以在以下链接找到:

  1. 揭开.NET 2.0配置之谜(一)
  2. 揭开.NET 2.0配置之谜(二)
  3. 揭开.NET 2.0配置之谜(三)
  4. 解码.NET 2.0配置之谜(一)
  5. 解码.NET 2.0配置之谜(二)
  6. 破解.NET 2.0配置之谜(一)

本文是6、破解.NET 2.0配置之谜(一) 的后续,由于原文比较长,故分成几部分翻译。本部分的主题如下:

3.  Configuration Representation

  1. ConfigurationElement
    • ConfigurationSection
    • ConfigurationElementCollection
  2. Non-Element Containers
    • ConfigurationSectionGroup
    • ConfigurationSectionCollection
    • ConfigurationSectionGroupCollection

Let's go on!

3、配置代表

配置管理,访问配置的第一步,离进程最重要的部分还很远。配置的终极目标是用简单、合乎逻辑的方式表示设置结构和设置数据。.NET 2.0配置框架的最终核心是ConfigurationElement  类。ConfigurationElement  类是对应到.config文件中一个实际的XML元素的代码。他提供代表配置元素,它的属性(attributes),以及各种元数据所必要的所有功能。如果不是全部,ConfigurationElement  也能方便大部分复杂需要,开发者要求足够的存储配置。

ConfigurationElementDiagram 图6  The ConfigurationElement and related classes(接上文编号)

在本系列前面的文章中,我们讨论了如何创建自定义ConfigurationSections  并解释了在对象模型配置系统的范围内属性(attributes)和元素是如何工作的。那些文章提供了一个非常基本的概述,配置是什么及ConfigurationElement 扮演什么角色。然而,ConfigurationElement 不仅仅是访问你定义在一个XML文件中的设置的一种手段。在本节中,我将提供一个底层的视野,ConfigurationElement 类、它的衍生类、作为配置创建者和使用者它提供给你的功能。

ConfigurationElement 类的主要目的是提供访问强类型、验证配置设置。那些设置也许会很简单,存储在当前元素的属性中;也许会很复杂,存储子配置元素中。然而,配置表示只是ConfigurationElement 提供访问的一半。几种配置的元数据也可以通过ConfigurationElement 访问,包括当前配置的上下文、元素和属性的锁信息、XML的源信息、可能的解析错误清单。ConfigurationElement 类也提供几种序列化和反序列化进程的钩子。元数据和序列化的细节将在下面的两节讨论。

.NET 2.0配置框架允许一个配置“规范”用下两种方法之一定义:声明式和命令式。声明式方法是通过放置属性(attributes)到类的属性(properties),描述了如何将属性(property)对应到XML文件的一个元素。命令式是通过在静态构造器中编程地预定义属性(properties)。最后,这两种方法完成了同样的事情,而它们是如何完成的却很不一样。声明式方法通常被认为较简单,程序员需要较少的工作。然而,这种实现简单是以当一个配置元素刷新时更高的处理成本为代价的。

3.1、ConfigurationElement

ConfigurationElement 类的功能大致可分为四组:解析(序列化和反序列化)、配置基础结构、设置数据和元数据。如果你看了图6,您可能也注意到了,这个的绝大部分是非公有的。ConfigurationElement 类的大部分方法和属性其衍生类才能访问,仅留下锁信息、索引器和IsReadOnly 是公有的(public)。受保护的(protected)大部分方法也被标记为虚拟的(virtual),允许大量的扩展,超出已经在前面的文章中讨论 了。后面几节专门讨论解析和配置元数据,定义和访问配置设置数据已经在前面的文章中讨论了,这里就不重复了。然而ConfigurationElement 类的最后一方面,基础结构(Infrastructure ),很少需要,但是他可以解决许多小问题。我们将要讨论ConfigurationElement 的基础结构的方法如下:

  • void Init()
  • void InitializeDefault()
  • void IsModified()
  • void ResetModified()
  • void IsReadOnly()
  • void SetReadOnly()
  • void ListErrors(IList errorList)
  • void SetPropertyValue(ConfigurationProperty prop, object value, bool ignoreLocks)
  • void Reset(ConfigurationElement parentElement)
  • void Unmerge(ConfigurationElement sourceElement, ConfigurationElement parentElement, ConfigurationSaveMode saveMode)

初始化(Initialization)

尽管他们的文档少得可怜,Init()InitializeDefault()方 法有些具体的和经常必要使用的。作为配置子系统基础结构的一部分,这些方法,没有明显的公开价值,但在序列化和其他处理内部调用。因为 ConfigurationElement中的数据缓存在内存中,可能会持续很长时间,可能会初始化不止一次由于保存或重置。构造器中基本的初始化有时不 足于确保ConfigurationElement的内部状态在整个生命周期中妥善维持。为了确保您的ConfigurationElement的内部状 态保持并在适当的时候更新,初始化应该在这些方法中的一个或这两个中执行。

Init()方法是最通常调用的初始化方法,而且是通常自定义初始化驻留的地方。当在我们自己的类中执行Init()方 法,必须注意采取适当的跟踪ConfigurationElement是否已经被初始化与否。重新初始化ConfigurationElement的单个 实例是非常罕见的,但Unmerge操作(见下文的讨论)和重置经常重复ConfigurationElements和从原来的填充他们。当初始化状态对 后续操作很重要,执行Init()方法是必不可少的。知道这个方法什么时候被调用对理解如何正确执行它很重要,它被调用的主要原因如下:

  • 在节反序列化时
  • 在创建元素初期,还没有设置值前
    • 总发生在ConfigurationSection序列化,Unmerge前
    • 可能发生在ConfigurationElement第一次由父元素的属性(property)创建,填充数据前
  • 当新的元素在内部被创建和加到ConfigurationElementCollection时
    • 可以发生在反序列化、元素重置、或Unmerge操作期间
  • 当元素手动添加到ConfigurationElementCollection时

虽然理论讨论如何和何时Init()方 法被调用对填补漏洞和回答问题很重要,但没有回答每个问题。对那些需要一个更完全地理解方法的功能和可能用法的人,我推荐使用Reflector去检查 System.Configuration中的KeyValueConfigurationElement 和KeyValueConfigurationCollection 类。这两个类提供最明显的例子,何时初始化可能发生比其他对象构建更清楚和在.NET 2.0框架中一个实例的Init是何如执行的。(译注:原文是These two classes provide the clearest example of when initialization may happen at times other than object construction and one instance of how Init was implemented in the .NET 2.0 framework.感觉这里翻译的不好,不理解的看原文)

不像Init方法,他可能会在任何响应各种触发器的时候被调用,InitializeDefault()方 法仅在一个实例中调用。每当一个方法被重置时,通常为响应一个Unmerge操作,调用InitializeDefault的是 ConfigurationElement层次(一般当它是一个ConfigurationSection)的根元素(或孤立元素)。重置是另外一个可重 写的接触结构方法之一,然而你可能会希望调用InitializeDefault,在您自己对重置响应时。

修改的和只读的状态(Modified and ReadOnly States)

修 改的和只读状态时相当不言而明的和通用对象状态。关于配置框架,然而,它有助于理解合适修改的和只读状态被设置和重置。修改的状态仅被 SetPropertyValue() 方法设置为TRUE,被ResetModified() 方法设置为FALSE。ResetModified() 通常在一个配置节保存时被调用。

SetReadOnly() 方法在初始化配置节设置时被调用,并且总是被调用。这个方法是为什么最常见的关于保存配置之一,只读异常产生的原因。除少数情况下,以读写方式访问一个配 置节和他的元素的唯一方法是,通过ConfigurationManager 或WebConfigurationManager 类的Open*Configuration() 方法直接打开一个配置文件。另外一个什么地方调用这个方法的例子是,重写对象 ConfigurationSection.GetRuntimeObject()的实现。这个方法仅有两个已知的使 用,AppSettingsSection 和ConnectionStringsSection 类,为公开AppSettings and ConnectionStrings 集合的只读版本通过ConfigurationManager 类(使用ConfigurationManager 访问所有的配置数据总是只读的)。

配置错误列表(Listing Configuration Errors)

ListErrors() 方法只有一个简单的用途。在配置框架内部,它用来提供一个错误列表(准确地说是(ConfigurationException 实例),当发生问题时抛出异常。你可以从写这个方法加入你自己的错误列表到集合中。这个列表仅在两个方法中被填充:节反序列化期间(解析错误一半不会单独 抛出,而收集在一起后包装在ConfigurationErrorsException中抛出),或通过 ElementInformation.Errors 属性(property)调用。在自定义反序列化时,最常见的时间错误可能会被添加到这个列表,将在稍后详细讨论。

译注:以下是我加的,非来自原文。

将此 ConfigurationElement 对象以及所有子元素中无效属性的错误添加到传递的列表中。

参数
errorList
类型:System.Collections..::.IList

实现 IList 接口的对象。

设置属性值(Setting Property Values)

SetPropertyValue() 方法也很有限的自定义使用,但一个非常具体的能够解决一些问题。一般来说,这个方法在内部使用,在自定义配置类中定义的一个配置属性被设置时。通常的用 法,它的第三个参数配置值为FALSE,确保任何应用在属性上的锁,在值改变前配置元素起作用。一些情况,然而,可能需要一个属性被设置不管锁(译 注:regardless of locks)(如在重置期间)。自定义使用SetPropertyValue 的最好例子是,AppSettingsSection.Reset 重写,对那些实际的例子感兴趣。

译注:以下是我加的,非来自原文。

将属性设置为指定值。

参数
prop
类型:System.Configuration.ConfigurationProperty

要设置的元素属性。

value
类型:System.Object

要分配给属性的值。

ignoreLocks
类型:System.Boolean

如果对属性的锁定应该被忽略,则为 true;否则为 false。

重置(Resets)

元 素重置是一个基本处理,恢复到默认锁信息和重新申请继承锁信息和设置属性值到那些之前被加载的配置文件。一个ConfigurationSection 调用reset将级联到所有的子元素,等等递归地,迫使所有的元素构成的配置节重置。在大多数情况下,理解Reset如何工作的无关紧要。配置通常用于只 读,或配置必须能读/写,大部分的需要是基本的。然而,截获一个Reset调用和修改它经常需要,当数据存储在配置属性以一种不同的形式缓存(i.e. 一个编码字符串的解码和存储要根据要求,当节重置时需要丢弃缓存副本)。配置元素重置的基本工作,通常不需要修改,但像要手动删除缓存数据的情况,有时候 必须加强以确保内容全面,正确地重置。Reset方法仅在以下两种情况下被调用…在Unmerge操作期间,或各种创建配置元素的方法时。

译注:以下是我加的,非来自原文。

此方法的默认行为是清除集合所包含的所有修改过的元素,并将修改过的元素设置为其父配置文件所指定的值。如果集合中的任一元素包含子元素,则还会对这些子元素调用此方法。

参数
parentElement
类型:System.Configuration..::.ConfigurationElement
返回当前元素的父 ConfigurationElement 对象,如果当前元素为顶级,则返回 nullNothingnullptrnull 引用(在 Visual Basic 中为 Nothing)。

Unmerge操作(The Unmerge Operation)

Unmerge 操作时另外一个基本处理,当一个配置节保存时执行。你应该还记得本文前面讲了配置文件是分层的,底层配置文件将合并高层配置文件。这个合并提供了一个统一 的视图,所有的配置设置从机器级到特定用户配置(或web环境下,通过特定web站点到web应用程序)配置节定义在一个较低级别可能在更高层次上改变他 们的设置。配置元素集合可能由上级删除、修改、甚至清除。当从事应用程序中的配置,这种合并视图是的使用配置非常简单,消除了完全理解配置设置来自哪的需 要。然而,当谈到保存配置设置,在运行时被修改了,这个合并视图必须适当的unmerge。

考虑到,这取决于您使用和保存的配置层次级 别,您可以添加,删除,清除和修改设置,这些设置可能已经存在或可能还不存在于你使用的层次级别(但应保存回当前的层次),unmerge操作是相当复 杂。unmerge是如何运作的具体细节将不会在这里讨论(因为我也不是完全清楚确切的运行机制),但足以说,这个过程处理分离的继承设置,从低层次到当 前工作层次。更重要的是,理解上文所述的许多方法在unmerge期间被调用,因为当前的元素往往是可克隆、重置或初始化,且只有适当的数据应保存重新填 充。什么数据依赖于你选择的ConfigurationSaveMode ,当调用Configuration.Save() 时。

译注:以下是我加的,非来自原文。

此方法可反转从配置层次结构的不同级别合并配置信息的效果。这将允许在序列化之前,当前层次结构级别上的配置设置与父级别上的配置设置有所不同。

系统会对新的临时元素调用此方法,以将父对象与源对象进行比较。然后根据 saveMode 值对临时对象进行设置,使其包含必须序列化的数据。

参数
sourceElement
类型:System.Configuration.ConfigurationElement
当前级别上的一个包含属性合并视图的 ConfigurationElement
parentElement
类型:System.Configuration.ConfigurationElement
返回当前元素的父 ConfigurationElement 对象,如果当前元素为顶级,则返回 nullNothingnullptrnull 引用(在 Visual Basic 中为 Nothing)。
saveMode
类型:System.Configuration.ConfigurationSaveMode
一个可确定要包含哪些属性值的 ConfigurationSaveMode 枚举值。

3.1.1、ConfigurationSection

ConfigurationSection 是ConfigurationElement类的衍生类,这意味着他们可能表现一样,包含它自己的属性(attributes)和子元素。除了继承 ConfigurationElement所有的功能和核心的行为,ConfigurationSection添加了他自己特有的功能,配置文件的主要部 分的根节点。ConfigurationSection 添加的唯一的公共功能是SectionInformation 元数据属性(property)。这个属性,细节将在以后讨论,提供了一些配置节的详细信息,包括访问原始XML。 ConfigurationSection 添加了一些保护(protected)方法,包括DeserializeSection 和 SerializeSection。

ConfigurationSectionDiagram.png

  图 7: The ConfigurationSection class

DeserializeSection 方法简单地调用基类的ConfigurationElement.DeserializeElement() 方法。在衍生类中,这个方法可能重写,提供自定义反序列化处理。AppSettingsSection 重写了这个方法,提供一个更精练的版本,通过使用file=“”属性(attribute)扩展配置文件。自定义反序列化详细讨论将在本文后面讨论。 SerializeSection 方法更有趣一点。这个方法在调用Configuration.Save()时被调用,并执行数据验证,然后是unmerge,之后配置节的元素包含 unmerged版本被序列化。这个方法可能会在衍生类中重写,提供高级多文件配置,这也将在本文后面讨论。

3.1.2、ConfigurationElementCollection

ConfigurationElementCollection, 像ConfigurationSection一样,也是ConfigurationElement的衍生类。 ConfigurationElementCollection在前面的文章已经讨论过了,因此他的详细讨论将不包括在这里。审查这个类图将提供,这个类 提供给衍生集合的功能的清晰视图,最显而易见的是Base*方法。一个需要注意的方法是 OnDeserializeUnrecognizedElement()重写。当ConfigurationElement反序列化代码碰到一个元素的名 字没有直接对应到一个ConfigurationProperty时,这个方法将被调用。因为 ConfigurationElementCollection类允许你自定义添加,删除和清除元素名字,这个方法的重写必须正确处理这些元素。我推荐对 如何处理无法识别元素的人,不管什么原因,用Reflector审查这个方法的代码。

image

图8  The ConfigurationElementCollection class

3.2、非元素容器(Non-Element Containers)

除 了类直接对应到*.config文件的配置元素,也有一些基本相对松散组织的类对应到节组。不像ConfigurationSection,加载 ConfigurationSectionGroup 后,没有直接映射到一个配置元素且不可以直接保存。一个节组和相关的子集合,被填充直接来自一个配置记录,它是一个类,这个类是隐藏框架工厂的一部分,记 录和各种支持类型用于处理原始的XML数据和生产配置节。其他两个类,ConfigurationSectionCollection 和 ConfigurationSectionGroupCollection用跟ConfigurationSectionGroup同样的方式处理。考虑 一大块处理XML的代码,为这些元素及填充他们是隐藏的,关于他的详细讨论可能是不需要的。他们简单地提供了一个有组织的结构,允许配置文件更清楚和更容 易维护。

image 图9  The ConfigurationSectionGroup and related classes

 

声明:此文是译文,实因水平有限,若有翻译不当之处请不吝指出,以免此译文误导他人,在此谢过! 未完....请继续关注,如果您觉得还不错望不吝推荐,能让更多的人看到,使开发者们更轻松的使用.NET 2.0的配置框架——这是Jon Rista的目的,也是我翻译这个的心愿!

英文原文:Jon RistaCracking the Mysteries of .NET 2.0 Configuration

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

分享: