NET的美妙特點之一是它的XML配置功能.在.NET 1.x時代,常見的應用程設置、資料庫連接字元串、ASP.NET Web伺服器配置和基本的自定義配置數據可以存儲在.config文件中.自定義配置節可以使用一些基本自定義結構,允許少數幾種信息存儲在.config文件中.然而更複雜的配置,最常見的實現是自定義XML結構和自定義解析代碼.儘管有多種不同性能的方法完成同樣的事情,這種代碼將變得相當複雜.
隨著.NET 2.0,自己編寫(可能很複雜、低性能、繁瑣)代碼來管理自定義XML配置結構的時代已經結束了..NET 2.0內置的XML配置子系統自定義配置能力已經大大革新,擁有一些非常有用的和節省時間的功能.幾乎任何XML配置結構你可能需要相對更少的工作且更容易.此外,反序列化.config中的XML總是可以重載的.這是的任何XML結構可以不失去.NET 2.0配置支持的其它高級功能.
本文繼續.NET 2.0的配置系列.本文的目的揭露.NET 2.0配置框架的所有細節和內部運作機制使開發人員能夠更好地利用它提供的廣泛功能.如果您是.NET 2.0配置的新手,或尚未掌握類型驗證和轉換的概念,您應該閱讀以前的文章,可以在以下鏈接找到:
請注意,本文提供的代碼示例只對文中提出的各點澄清.在前面兩篇本系列文章提供可編譯和可下載的代碼示例(譯註:代碼請到原文鏈接下載).這篇文章的目標是比前面兩篇文章講解更詳細且更高級,而不是提供可編譯,可運行的代碼示例.相反,我們的目標是揭露.NET 2.0配置框架的隱含核心理論和重要的細節.期望的結果是,讀完本系列的文章後任何人都有興趣使用.NET 2.0配置.
歡迎來到.NET 2.0配置之謎系列的第三部分.本文將介紹.NET 2.0配置框架完整詳細的細節.我將揭露框架的鮮為人知的特性、功能、怪癖和內部運作機制.幾個架構圖將展示以提供將要討論的細節可視化的支持.
(譯註:上面這些英語很簡單,我這裡就不翻譯了,見下面的標題.5、Configuration Serialization,6、Web Configuration and LocationsJon Rista還沒有寫完,似乎也不準備寫了.)
許多形式的配置可用於現代的.NET 2.0應用程序.二進位數據、INI文件、資料庫、XML、甚至任意文本結構.根據環境、應用類型、使用因素等等,你的配置存儲需求可能會改變.在大多數情況下,大部分.NET應用(以及編寫他們的程序員們)只需要能夠存儲配置……並不真正關心怎麼存儲.大多數配置往往希望是分層的和可手動編輯的.這是的XML是一個理想的平台,在其上建立一個配置的存儲框架.這也是的.NET 2.0的配置框架對.NET 2.0開發者非常有吸引力.
“分層”是重要的方面,當涉及到.NET 2.0配置框架時,並在不止一個方面.除了作為XML的一個應用外,且提供了一個分層的媒介存儲實際配置設置,.NET 2.0配置框架在自己的權利範圍是分層的(譯註:the .NET 2.0 configuration framework is hierarchical in it's own right).根據應用程序的內容,多個配置文件的層次秩序自然是存在的且當代碼請求配置時會合併在一起.這種分層結構的配置文件允許設置應用在各級,從整個機器到應用程序,甚至到個別用戶或資源請求.
.NET 2.0配置框架的分層結構可以在下面的圖中看出.這個圖有幾個重點需要注意,配置文件合併的秩序及不同配置環境中可用的配置.根據環境,什麼可以配置這些設置,以及那些設置是如何合併的,是不同的.
圖1 Configuration File Hierarchy
在.NET的世界中,有兩種主要類型的應用程序:Windows應用程序和Web應用程序.Windows應用程序,或可執行文件,有一個相對簡單的4層配置結構和合併過程.另一方面,Web應用程序,為特定的位置應用和合併配置,有一個更複雜的結構.這兩個主要類型的應用程序都創建一個配置上下文,管理如何應用配置.
獨立於上下文的是機器級(machine-level)配置.機器級配置應用於任何運行在CLR里的應用程序,以及.NET框架自己使用的所有其他配置選項.他可能令許多人吃驚,但是.NET框架使用跟本系列討論的同樣的配置框架.可能更吃驚,沒有配置功能在.NET框架中觀察到的是特別的或專門的……你可以做同樣的事情.
快速檢查machine.config文件(在%SYSTEMROOT%\Microsoft.NET\Framework\v2.0.50727\CONFIG下能找到)將證明,這裡定義了所有的“預配置”或“默認”ConfigurationSectionGroup和ConfigurationSection元素.諸如此類appSettings, connectionStrings, system.web等等.許多默認設置,例如encryption providers、默認ASP.NET memebership、profile和role providers等等,都應用在這裡.在相同的目錄下,應該也注意到這個文件的comment版、default版(譯註:即machine.config.comments,它包含有用的註釋;machine.config.default,包含默認設置.配置系統不使用這些文件來配置應用程序.)、以及一些不同安全級別的默認web.config文件.然而編輯machine.config文件是危險的,也是唯一的方法應用設置全局到每個應用程序.在機器(machine)級下,配置分成兩種可用的上下文(情況)……Exe和web.
Exe上下文對任何可執行應用程序可用.這包括Windows Forms,Windows Services 和Console applications.在此上下文中,核心配置基礎理解成4個級別的配置:Machine、Application、Roaming User 、User.在“上下文”中每個級別按序更具體.因此,低級別可以重寫高級別定義的設置.值得著重注意的是,Roaming User配置比User配置級別更高.這允許用戶特定設置存儲在台式機和筆記本電腦的是相互獨立的(譯註:This allows user-specific settings to reside on both a desktop and a laptop independent of each other),而Roaming設置是在兩者之間共享的且都可用的.
Web上下文僅僅對運行在ASP.NET主機上(不一定要有IIS…任何ASP.NET都可以,包括VS2005開發伺服器或使用System.Web.Hosting.ApplicationHost的客戶web伺服器)可用.不像Exe上下文,以用戶相關,web上下文視位置而定.配置應用於特定的web站點位置,可能顯示地配置在一個web.config文件,或隱式地從多個web.config文件合併.基於配個用戶基礎的配置在web配置上下文通常是不可行的,即使用戶是合理認證的.
.NET 2.0配置的分層性質提供了很大程度的靈活性,允許特定的用戶或位置有自己的配置設置.然而,那些配置不是孤立的,多個層次上重複的設置可以在低層次上重寫.我們可以在圖1中看到,大部分較具體的配置文件合併到較不具體的,最具體的設置重寫了最不具體的.在Exe上下文中,用戶(或者更準確地說,本地用戶)的設置時最具體的規定,隨後是漫遊用戶(在兩個或更多機器上共享),應用程序,是機器.
在web上下文中,合併稍微有點複雜.除了多個*.config文件從更具體的位置(i.e.\wwwroot\siteA\web.config is more specific than \wwwroot\web.config when merged)合併,一個應用程序特定位置的配置可以直接定義在單個web.config文件.web.config和位置合併之間的細微差別將在本文的後面更詳細地討論.
.NET 2.0的配置框架自身相當複雜,然而卻提供了快速、簡單的方法實現自定配置.配置框架不僅提供實現自定義配置的方法,提供方法實現自定義數據驗證和轉換、自定義providers,公開的自定義序列化和反序列化鉤子,甚至允許配置進行加密.關於配置元素的元數據(Metadata)也是公開的,提供關於載入什麼數據、從哪載入數據、怎麼載入數據的細節..NET 2.0的配置框架的體系結構顯示在圖2中.圖中每個特定的元素是本文你進一步將讀到的主題.
圖2 Configuration Framework Architectur
在這個看起來複雜的圖背後是一個優美的、合乎邏輯和有效地配置管理系統.我試圖將這些體系架構中單獨的組件組織成邏輯組,形成了本文的核心部分.
第一個要詳細討論的概念,邏輯上也是你的項目配置工作開始的地方.配置管理,在本文的範圍,是指查找配置(直接或映射到特定配置文件),檢索配置節和使用時存儲那些配置節.配置文件可能會被直接載入,提供額外的功能和允許特定*.config文件(VS. 默認映射的)被載入.映射到特定配置文件根據你操作的上下文不同而不同,但是允許任何配置文件被載入,提供必要的賦予許可權讀取資源.
ConfigurationManager和後面的WebConfigurationManager,是訪問.NET 2.0配置主要的出發點.ConfigurationManager提供了所有核心服務,檢索配置節和包含在其中的設置,也提供方法允許配置文件被顯示地載入在Exe上下文中.WebConfigurationManager提供了額外的方法顯示地載入配置,在web上下文中,同時也作為使用ConfigurationManager的方法的代理.這兩個靜態類見下圖,以供參考.
圖3 ConfigurationManager and WebConfigurationManager
ConfigurationManager類提供的主要功能,GetSection(string sectionName),已經在此系列前面的文章詳細描述了,這裡將不重複了.默認情況下,ConfigurationManager類提供隱式地只讀訪問配置.通常,配置擴展簡單的只讀和保存也是需要的.ConfigurationManager公開的幾種方法,允許配置文件在一個明確的上下文中打開.第一種方法是使用ConfigurationManager.OpenExeConfiguration()方法.他們提供讀/寫訪問配置文件,通過配置類.
有兩個重載的OpenExeConfiguration()方法.一個重載採用一個字元串表示當前運行可執行文件路徑作為參數,另外一個採用ConfigurationUserLevel枚舉值作為參數.第一種方法你提供的文件名要附加“.config”並載入配置文件.要著重注意的是,OpenExeConfiguration(string exePath)方法是一個非常誤導人的方法,文件名不一定得是.exe執行文件.配置發燒友的一個重大的發現(不可達的,但…基於我的Internet搜索)可以通過這個方法,提供它的正確使用.考慮一下情形:
上述方案,經常是足夠了,允許多個配置文件(針對任何程序集,不僅僅是主要的exe)同時使用.儘管OpenExeConfiguration(string exePath)方法調用了一個DLL文件,但配置文件被ConfigurationManager訪問不改變.任何其他代碼訪問存儲在Application.exe.config 文件中的配置繼續不加改變或衝突的訪問.這個概念的簡單證明,可以體現在以下代碼:
- //AssumesLibrary.dllanditscorrespondingLibrary.dll.configfileexist,
- //arereferencedandproperlycolocatedwiththe.exe
- Console.WriteLine("App(before):"
- ConfigurationManager.AppSettings["test"]);
- Console.WriteLine("LoadingLibrary.dll.config...");
- Configurationother=
- ConfigurationManager.OpenExeConfiguration("Library.dll");
- Console.WriteLine("App(after):"
- ConfigurationManager.AppSettings["test"]);
- Console.WriteLine("Lib(after):"
- other.AppSettings.Settings["test"].Value);
- //Expectedoutput
- App(before):application
- LoadingLibrary.dll.config...
- App(after):application
- Lib(after):library
- <!--Application.exe.config-->
- <configuration>
- <appSettings>
- <addkey="test"value="application"/>
- </appSettings>
- </configuration>
- <!--Library.dll.config-->
- <configuration>
- <appSettings>
- <addkey="test"value="library"/>
- </appSettings>
- </configuration>
儘管它的誤導性的簽名和名字,OpenExeConfiguration(string exePath)是一個非常有效的方法解決大部分頻繁配置的問題.這個方法還能從HTTP路徑載入配置,可能是一個智能客戶端使用ClickOnce部署的方案..用OpenExeConfiguration(“http://someserver.com/clickonce/someapp.exe”)
,*.config文件為一個ClickOnce部署的應用程序可能會被確定和從它的正確文件夾載入(這是在安裝和更新時自動生成的).
第二個方法,OpenExeConfiguration(ConfigurationUserLevel level)將載入指定層次是的那個的配置文件.配置層次,在exe上下文可用,允許你指定任意你想要的exe、漫遊用戶、本地用戶的配置. ConfigurationUserLevel 枚舉也有一點令人誤解,從它的值命名.它能導致誤解這個方法做什麼的,及期望返回什麼類型的配置.它每個值的真實含義如下:
記住配置是分層的和可合併的.當請求漫遊用戶或本地用戶的配置時,向上到
machine.config將被合併,在給定用戶級別應用程序可以訪問結構完整的配置.一個有趣的結果是,這個合併允許你請求漫遊用戶或本地用戶的配置,甚至當user.config文件不存在時.返回的配置對象將包含一個不存在的FilePath和HasFile屬性是false.漫遊和本地用戶配置設置是有趣的東西.一些.NET 2.0配置的微妙的行為變得有條理(或抬起他們醜陋的頭,取決於你怎麼看).儘管他們醜陋的頭,這些微妙的行為是另一個在自定義方法中使用.NET 2.0配置的重要原因..NET配置框架提供一些擴展的安全特性,允許鎖定設置、節、甚至節組(絕對地,或根據他們訪問的等級).考慮以下代碼:
- ConfigurationroamingCfg=
- ConfigurationManager.OpenExeConfiguration(
- ConfigurationUserLevel.PerUserRoamingAndLocal);
- CustomSectioncustomSection=
- roamingCfg.GetSection("customSection")asCustomSection;
- if(customSection==null)
- {
- customSection=newCustomSection();
- roamingCfg.Sections.Add("customSection",customSection);
- }
- customSection.CustomValue="localuservalue";
- customSection.Save(ConfigurationSaveMode.Minimal);
這個例子應該打開本地User.config文件並獲得(或建立,如果他不存在)一些CustomSection
.這個自定義配置節的值是可編輯的和可保存修改.最終目標是載入任何現有的設置、或用新節和設置建立本地User.config文件,如果他不存在.這似乎很簡單,但取決於“CustomSection
”是否曾在roaming、exe、machine level定義,加配置節或編輯它,如果它已經存在,可能是不可能的.這種情況可能發生比較頻繁,可能會變得非常複雜,當大量使用配置.原因和解決編輯/保存用戶配置級別的配置的方案的詳細資料,將在本文的配置元數據部分討論.
除了剛才談到的,存在一些其他的方法打開配置文件.不像OpenExeConfiguration()方法,它做了幾個關於配置文件存儲在哪的假定,OpenMappedExeConfiguration()
和 OpenMappedMachineConfiguration()允許你顯示地指定你的*.config文件存儲在磁碟哪.使用這些方法,你能載入一個備用的machine.config,從你自己選定的位置載入User.config文件(vs.讓.NET框架決定路徑)等等.當訪問machine.config,自定義版本不是必須的,應該使用
OpenMachineConfiguration()
.關於如何使用這些方法和相應的ConfigurationFileMap類的詳細信息,將在ConfigurationFileMap類子部分.
ConfigurationManager類一個公開的方法,RefreshSection(string sectionName)
,是另一個我經常被問到的問題之一的答案.上面描述的眾多OpenConfiguration方法允許配裝備打開讀/寫及保存改變.有些時候,然而,保存修改到配置通過ConfigurationManager類得不到相應的方法(特別是在web環境下).有許多方法來解決這些問題,但最簡單的方法保存之後立即就是用合適的節名字調用RefreshSection.這強迫(在大多數情況下……極少數情況下我報告他已經沒有效了)ConfigurationManager下次調用GetSection() 時從硬碟載入和解析特定的配置節.
ConfigurationManager類,是訪問Exe上下文中配置的第一步,在Web上下文中它是遠遠不夠的.不像可執行文件,web應用程序沒有一個確定的本地用戶及那些可能創建的User.config文件.事實上,在web應用程序中沒有特定用戶的配置.儘管如此,考慮特定位置的配置時,web配置可以是一個更複雜的的東西.特定位置配置及如何使用WebConfigurationManager以充分利用它,是本文中將討論.一個完整的WebConfigurationManager和ConfigurationLocations的討論將在本文後面部分討論.
ConfigurationFileMap 是ConfigurationManager的OpenMappedExeConfiguration 和 OpenMappedMachineConfiguration方法的必要組成部分.這些類允許指定特定路徑的*.config文件和當創建一個配置對象時OpenMapped 方法將執行所有合適的合併.ConfigurationFileMap 類表示一個機器配置文件映射和需要調用OpenMappedMachineConfiguration.此外文件映射類,Exe上下文的ExeConfigurationFileMap,Web上下文的WebConfigurationFileMap ,機器級除外都需要載入配置.
圖4 The ConfigurationFileMap class hierarchy
ExeConfigurationFileMap 允許你對專門配置機器的準確路徑名、exe、漫遊用戶、本地用戶配置文件,一起或零星的,當調用OpenMappedExeConfiguration()時.你不必指定所有的文件,但當配置對象創建時所有的文件將被確定及合併. 如果您指定一個自定義exe和本地配置文件,但不指定機器和漫遊文件,默認機器和漫遊文件將被找到並與指定exe和用戶文件合併.這可能帶來意想不到的後果,如果指定文件沒有與默認文件保持適當的同步.
- stringappData=Environment.GetFolderPath(
- Environment.SpecialFolder.ApplicationData);
- stringlocalData=Environment.GetFolderPath(
- Environment.SpecialFolder.LocalApplicationData);
- ExeConfigurationFileMapexeMap=
- newExeConfigurationFileMap();
- exeMap.ExeConfigFilename=
- "C:\Application\Default.config";
- exeMap.RoamingUserConfigFilename=
- Path.Combine(appData,@"Company\Application\Roaming.config");
- exeMap.LocalUserConfigFilename=
- Path.Combine(localData,@"Company\Application\Local.config");
- ConfigurationexeConfig=
- ConfigurationManager.OpenMappedExeConfiguration(
- exeMap,ConfigurationUserLevel.None);
- ConfigurationroamingConfig=
- ConfigurationManager.OpenMappedExeConfiguration(exeMap,
- ConfigurationUserLevel.PerUserRoaming);
- ConfigurationlocalConfig=
- ConfigurationManager.OpenMappedExeConfiguration(exeMap,
- ConfigurationUserLevel.PerUserRoamingAndLocal);
- Console.WriteLine("MACHINE/EXE:" exeConfig.FilePath);
- Console.WriteLine(
- "MACHINE/EXE/ROAMING_USER:" roamingConfig.FilePath);
- Console.WriteLine(
- "MACHINE/EXE/ROAMING_USER/LOCAL_USER:" localConfig.FilePath);
如果ConfigurationManager 類沿著是綠野仙蹤(譯註:綠野仙蹤——the Yellow Brick Road to the Emerald City,應該都知道吧,不知道的去Google或百度)到配置第一步,那麼Configuration 類絕對就是第二步.由ConfigurationManager 類公開的OpenConfiguration 方法之一調用將返回一個Configuration 對象.Configuration 對象代表合併的配置,用戶級別、位置、你請求的文件映射的任何配置.不像ConfigurationManager ,Configuration 類公開配置節的讀/寫模式的完整詳細細節.節和節組可以創建、刪除和調整特們的安全設置,當通過Configuration 類訪問時.
圖5 The Configuration class
審查圖5后,你應該看到Configuration 類比ConfigurationManager 類公開更多的信息和功能.一些信息是相同的,包括AppSettings ,ConnectionStrings , GetSection()方法.除了GetSection()方法外,Configuration 類也公開了GetSectionGroup(string sectionGroupName)方法,它允許您載入定義在配置文件中的ConfigurationSectionGroup 類.Configuration 類也公開了所有定義的ConfigurationSections 和 ConfigurationSectionGroups集合.Configuration 類也公開了重載的Save 和 SaveAs 方法,允許修改保存回現有的配置文件,或者創建一個新的.從前面的文章,你應該已經熟悉了載入和保存配置節和節組.
Configuration 類的一些特性還沒有討論,他們不是很明顯,成為可能的節和節組.除了讓你載入和保存已有的配置節和節組,你也可以添加和刪除節組.這是一個強大的功能,允許一個配置文件用代碼編程地創建.當漫遊或本地用戶的配置,需要設置基本應用程序功能不必要的配置時,這是非常有用的.用這種方式創建時,一個重要因素要注意.默認,節可能只被定義在machine和exe級.如果您需要添加新的配置節,甚至這個節將只在漫遊或本地用戶的*.config文件使用,你必須先在exe添加節,然後在用戶級修改節設置.考慮以下代碼:
- ConfigurationexeConfig=
- ConfigurationManager.OpenExeConfiguration(
- ConfigurationUserLevel.None);
- if(exeConfig.GetSection("customSection")==null)
- {
- CustomSectionsection=newCustomSection();
- section.SectionInformation.AllowExeDefinition=
- ConfigurationAllowExeDefinition.MachineToLocalUser;
- exeConfig.Sections.Add("customSection",section);
- exeConfig.Save(ConfigurationSaveMode.Minimal);
- }
- ConfigurationuserConfig=
- ConfigurationManager.OpenExeConfiguration(
- ConfigurationUserLevel.PerUserRoamingAndLocal);
- CustomSectionsection=
- userConfig.GetSection("customSection")asCustomSection;
- section.SomeSetting="somevalue";
- userConfig.Save(ConfigurationSaveMode.Minimal);
上面的代碼首次執行前,EXE和用戶*.config文件應該向這樣:
- <!--EXE.configfile-->
- <configuration>
- </configuration>
- <!--USER.configfile-->
- <!--DOESNOTEXISTYET!-->
上面的代碼首次執行后,EXE和用戶*.config文件應該向這樣:
- <!--EXE.configfile-->
- <configuration>
- <configSections>
- <sectionname="test"type="Example.CustomSection,Example"
- allowExeDefinition="MachineToLocalUser"/>
- </configSections>
- </configuration>
- <!--USER.configfile-->
- <configuration>
- <test>
- <addkey="key"value="value"/>
- </test>
- </configuration>
定義的限額(AllowDefinition 和 AllowExeDefinition)是重要因素,當使用多層次配置時.有兩個前面提到的那些“微妙的行為”,使用.NET 2.0配置時會很複雜.詳細解釋定義限額和其他有類似微妙影響的設置將在本文的配置元數據那部分討論.
Configuration 類的一個重要屬性(property)是EvaluationContext 屬性(property).這個屬性公開了一個ContextInformation 類的實例,他提供訪問上下文對象和一個標誌指示配置對象代表machine.config,或一個應用程序,或用戶級*.config文件.上下文對象公開基本但有用的信息,可用於簡化任務,否則,可能需要更複雜的邏輯.上下文類的詳細解釋見本文的配置元數據部分.
聲明:此文是譯文,實因水平有限,若有翻譯不當之處請不吝指出,以免此譯文誤導他人,在此謝過! 未完....請繼續關注,如果您覺得還不錯望不吝推薦,能讓更多的人看到,使開發者們更輕鬆的使用.NET 2.0的配置框架——這是Jon Rista的目的,也是我翻譯這個的心愿!
英文原文:Jon Rista,Cracking the Mysteries of .NET 2.0 Configuration
[火星人 ] 破解.NET 2.0配置之謎(一)已經有472次圍觀