歡迎您光臨本站 註冊首頁

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

←手機掃碼閱讀     火星人 @ 2014-03-09 , reply:0

如果您是.NET 2.0配置的新手,或尚未掌握類型驗證和轉換的概念,您應該首先閱讀以前的文章,可以在以下鏈接找到:

  1. 1. 揭開.NET 2.0配置之謎(一)
  2. 2. 揭開.NET 2.0配置之謎(二)
  3. 3. 揭開.NET 2.0配置之謎(三)
  4. 4. 解碼.NET 2.0配置之謎(一)
  5. 5. 解碼.NET 2.0配置之謎(二)
  6. 6. 破解.NET 2.0配置之謎(一)

本文是6、破解.NET 2.0配置之謎(一) 的後續,由於原文比較長,故分成幾部分翻譯.本部分的主題如下:

3. Configuration Representation

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

Let's go on!

3、配置代表

配置管理,訪問配置的第一步,離進程最重要的部分還很遠.配置的終極目標是用簡單、合乎邏輯的方式表示設置結構和設置數據..NET 2.0配置框架的最終核心是ConfigurationElement 類.ConfigurationElement 類是對應到.config文件中一個實際的XML元素的代碼.他提供代表配置元素,它的屬性(attributes),以及各種元數據所必要的所有功能.如果不是全部,ConfigurationElement 也能方便大部分複雜需要,開發者要求足夠的存儲配置.

圖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 的基礎結構的方法如下:

  1. void Init()
  2. void InitializeDefault()
  3. void IsModified()
  4. void ResetModified()
  5. void IsReadOnly()
  6. void SetReadOnly()
  7. void ListErrors(IList errorList)
  8. void SetPropertyValue(ConfigurationProperty prop, object value, bool ignoreLocks)
  9. void Reset(ConfigurationElement parentElement)
  10. void Unmerge(ConfigurationElement sourceElement, ConfigurationElement parentElement, ConfigurationSaveMode saveMode)

初始化(Initialization)

儘管他們的文檔少得可憐,Init()和InitializeDefault()方法有些具體的和經常必要使用的.作為配置子系統基礎結構的一部分,這些方法,沒有明顯的公開價值,但在序列化和其他處理內部調用.ConfigurationElement中的數據緩存在內存中,可能會持續很長時間,可能會初始化不止一次由於保存或重置.構造器中基本的初始化有時不足於確保ConfigurationElement的內部狀態在整個生命周期中妥善維持.為了確保您的ConfigurationElement的內部狀態保持並在適當的時候更新,初始化應該在這些方法中的一個或這兩個中執行.

Init()方法是最通常調用的初始化方法,是通常自定義初始化駐留的地方.當在我們自己的類中執行Init()方法,必須注意採取適當的跟蹤ConfigurationElement是否已經被初始化與否.重新初始化ConfigurationElement的單個實例是非常罕見的,但Unmerge操作(見下文的討論)和重置經常重複ConfigurationElements和從原來的填充他們.當初始化狀態對後續操作很重要,執行Init()方法是必不可少的.知道這個方法什麼時候被調用對理解如何正確執行它很重要,它被調用的主要原因如下:

  1. 在節反序列化時
  2. 在創建元素初期,還沒有設置值前
    1. 總發生在ConfigurationSection序列化,Unmerge前
    2. 可能發生在ConfigurationElement第一次由父元素的屬性(property)創建,填充數據前
  3. 當新的元素在內部被創建和加到ConfigurationElementCollection時
    1. 可以發生在反序列化、元素重置、或Unmerge操作期間
  4. 當元素手動添加到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.

圖 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審查這個方法的代碼.

圖8 The ConfigurationElementCollection class

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

除了類直接對應到*.config文件的配置元素,也有一些基本相對鬆散組織的類對應到節組.不像ConfigurationSection,載入ConfigurationSectionGroup 后,沒有直接映射到一個配置元素且不可以直接保存.一個節組和相關的子集合,被填充直接來自一個配置記錄,它是一個類,這個類是隱藏框架工廠的一部分,記錄和各種支持類型用於處理原始的XML數據和生產配置節.其他兩個類,ConfigurationSectionCollection 和 ConfigurationSectionGroupCollection用跟ConfigurationSectionGroup同樣的方式處理.考慮一大塊處理XML的代碼,為這些元素及填充他們是隱藏的,關於他的詳細討論可能是不需要的.他們簡單地提供了一個有組織的結構,允許配置文件更清楚和更容易維護.

圖9 The ConfigurationSectionGroup and related classes

聲明:此文是譯文,實因水平有限,若有翻譯不當之處請不吝指出,以免此譯文誤導他人,在此謝過! 未完....請繼續關注,如果您覺得還不錯望不吝推薦,能讓更多的人看到,使開發者們更輕鬆的使用.NET 2.0的配置框架——這是Jon Rista的目的,也是我翻譯這個的心愿!

英文原文:Jon Rista,Cracking the Mysteries of .NET 2.0 Configuration

未完待續…請繼續關注,還有一篇就可以寫完了!


[火星人 ] 破解.NET 2.0配置之謎(二)已經有415次圍觀

http://coctec.com/docs/security/show-post-58897.html