在我看來,WEB project的開發(fā)與WINFORM的開發(fā)最大的區(qū)別在于web的運行是在Framework上更高一層框架上運行,即ASP。NET框架,程序員在web下的開發(fā)可以說是黑盒開發(fā),不是讓你去定義程序入口和執(zhí)行順序,而是asp.net來調(diào)用你的各個方法,程序員做的一切都是一種受控的舞蹈。就像我們調(diào)用nunit之類的工具來測試一個dll一樣,nunit是容器,是框架,執(zhí)行哪個方法是由nunt來決定的。因此,也就有了頁面執(zhí)行周期各狀態(tài)等令剛?cè)腴T的程序員困惑不已的事,其實,究其根源,在于不了解容器而去使用容器。對于asp.net框架的學(xué)習,我們不妨從配置文件開始。
對于程序開發(fā)者而言,寫配置文件是經(jīng)常性的工作,如果你寫了一個xx.config文件,如果沒有詳盡的注釋,別人恐怕很難讀懂,沒有良好的配置架構(gòu),程序也失去了活力。在我看來,.net配置文件的特點在于反射定義和繼承性。 我們訪問配置文件時經(jīng)常覺得配置文件的結(jié)構(gòu)不太符合我們的需要,我們需要從里面更方便地獲得自己定義的對象,而不僅僅是key和value,對于自定義配置文件的著述已有很多,在此不再描述,有興趣的朋友可以訪問 自定義配置節(jié)其實還是在.net配置文件架構(gòu)的應(yīng)用而已,我們先來搞懂配置文件的結(jié)構(gòu),弄清楚.net配置文件的運行方式。下面是machine.config的一部分內(nèi)容:
SDK中<section>的定義為: <sectionGroup>的定義為:
name="section name"
type="configuration section handler class, assembly"
allowDefinition="Everywhere|MachineOnly|MachineToApplication"
allowLocation="true|false" />
name="section group name"/>
</sectionGroup>
我們來看看.net框架內(nèi)是如何利用這種結(jié)構(gòu)的。反編譯System.dll找到GetConfig方法,在里面我們發(fā)現(xiàn)實際獲取config的工作默認是由實現(xiàn)了IConfigurationSystem的DefaultConfiguationSystem類來實現(xiàn)的。
{
if (!ConfigurationSettings._configurationInitialized)
{
lock (typeof(ConfigurationSettings))
{
if ((ConfigurationSettings._configSystem == null) && !ConfigurationSettings.SetConfigurationSystemInProgress)
{
ConfigurationSettings.SetConfigurationSystem(new DefaultConfigurationSystem());
}
}
}
if (ConfigurationSettings._initError != null)
{
throw ConfigurationSettings._initError;
}
return ConfigurationSettings._configSystem.GetConfig(sectionName);
}
我們再來看DefaultConfigurationSystem,這個類主要包含了machine.config的名稱路徑的基本信息和一些uri操作,而實際的GetConfig的操作交給了ConfiguationRecord來處理,這個類沒有實現(xiàn)任何接口,可見他和DefaultConfiguration是綁定在一起的。
現(xiàn)在我們可以專注于configuationrecord的具體實現(xiàn)了,load方法中得到一個xmltextwriter,并執(zhí)行.scanfactoriesrecursive和scansectionsrecursive方法。
scanfactoriesrecursive方法會調(diào)用他的一個重載方法來解析<configsections>中的<sectiongroup>,<section>,<remove>,<clear>,我們寫配置文件時大小寫可不能寫錯哦,.net沒有做toslower之類的轉(zhuǎn)換,直接就是 “== “。在這個方法里程序會將解析得到的sectiongroup以key=tagkey,value= ConfigurationRecord.GroupSingleton的方式存到EnsureFactories里,將section以key=tagkey,value=typestring的方式儲存,值得注意的是,這里并沒有創(chuàng)建實現(xiàn)IConfigurationSectionHandler的實例對象,而是將其類型名(比如:字符串”system.Configuration.NameValueSectionHandler”)作為值到EnsureFactories,等到后面GetConfig的時候再來反射創(chuàng)建。<remove>則存為ConfigurationRecord.RemovedFactorySingleton。<clear>就清空EnsureFactories。這里的tagkey是各級name的組合,比如”mygroup/mysection”這樣以分隔符”/”組合的形式。應(yīng)該客觀地說這部分代碼用了很多goto語句,可讀性不是太好,但這樣寫也確實沒有什么問題。
scansectionsrecursive方法會解析配置文件里的section實例,并將其tagkey儲存到hashtable _unevaluatedSections中,表示尚未evaluated的configkey的集合,可見section實例對象的創(chuàng)建和handler一樣,都是fetch when need。在后面的操作Getconfig中會使用他。
現(xiàn)在我們就可以看getconfig方法是怎么來執(zhí)行得到我們想要的對象的。
如果_result中有對應(yīng)configkey的section實例,就返回,沒有則需要對configkey進行resolveconfig,將解析到的對象保存到_result中并返回給用戶。在resolveconfig方法中,可以看到如果當前的配置文件中沒有要求的configkey就會返回父級的section實例,比如machine.config里的內(nèi)容。
然后就是evaluate及其后續(xù)操作了,主要就是將configkey分解成字符串數(shù)組,一層層地去找對應(yīng)的xmlelement,找到后傳給configkey對應(yīng)的handler,如果該handler沒有創(chuàng)建則反射創(chuàng)建,然后由該handler創(chuàng)建section的實例對象,返回給用戶,該部分關(guān)鍵代碼如下:
現(xiàn)在我們就明白了當我們用system..configurtion.configuationsetting.getconfig的時候發(fā)生過什么了。