|
在我看來,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é)習(xí),我們不妨從配置文件開始。 對于程序開發(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)容:
<configSections>
<section name="runtime" type="System.Configuration.IgnoreSectionHandler, System, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" allowLocation="false" /> <sectionGroup name="system.net"> <section name="authenticationModules" type="System.Net.Configuration.NetAuthenticationModuleHandler, System, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" /> </ sectionGroup> </configSections> SDK中<section>的定義為: <section
name="section name" type="configuration section handler class, assembly" allowDefinition="Everywhere|MachineOnly|MachineToApplication" allowLocation="true|false" /> <sectionGroup>的定義為: <sectionGroup
name="section group name"/> </sectionGroup>
我們來看看.net框架內(nèi)是如何利用這種結(jié)構(gòu)的。反編譯System.dll找到GetConfig方法,在里面我們發(fā)現(xiàn)實際獲取config的工作默認是由實現(xiàn)了IConfigurationSystem的DefaultConfiguationSystem類來實現(xiàn)的。 public static object GetConfig(string sectionName)
{ 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是綁定在一起的。 1internal class DefaultConfigurationSystem : IConfigurationSystem
2{ 3 // Methods 4 internal DefaultConfigurationSystem(); 5 object IConfigurationSystem.GetConfig(string configKey); 6 void IConfigurationSystem.Init(); 7 8 // Properties 9 internal static Uri AppConfigPath { get; } 10 internal static string MachineConfigurationFilePath { get; } 11 internal static string MsCorLibDirectory { get; } 12 13 // Fields 14 private ConfigurationRecord _application; 15 private const string ConfigExtension = "config"; 16 private const string MachineConfigFilename = "machine.config"; 17 private const string MachineConfigSubdirectory = "Config"; 18 private const int MaxPathSize = 0x400; 19} 20 事實上所有的配置文件的分析和獲取都是在ConfiguationRecord里實現(xiàn)的,作為配置文件分析的第一步,正如我們經(jīng)常做的一樣->加載一個配置文件,這個方法公開為 Load(filename)。DefaultConfiguationSystem的Init()方法中用machine.config創(chuàng)建了一個ConfiguationRecord對象,并將其作為父對象傳遞給當前程序的ConfiguationRecord對象,當然前提是當前程序有配置文件,比如myapp.config,然后再加載當前程序的配置文件,從而實現(xiàn)配置文件的繼承。 void IConfigurationSystem.Init()
{ lock (this) { if (this._application == null) { ConfigurationRecord record1 = null; string text1 = DefaultConfigurationSystem.MachineConfigurationFilePath; Uri uri1 = DefaultConfigurationSystem.AppConfigPath; this._application = record1 = new ConfigurationRecord(); bool flag1 = record1.Load(text1); if (!flag1 || (uri1 == null)) > { return; } this._application = new ConfigurationRecord(record1); this._application.Load(uri1.ToString()); } } } 現(xiàn)在我們可以專注于configuationrecord的具體實現(xiàn)了,load方法中得到一個xmltextwriter,并執(zhí)行.scanfactoriesrecursive和scansectionsrecursive方法。 reader1 = ConfigurationRecord.OpenXmlTextReader(filename);
if (reader1 != null) { this.ScanFactoriesRecursive(reader1); if (reader1.Depth == 1) { this.ScanSectionsRecursive(reader1, null); } return true; } 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語句,可讀性不是太好,但這樣寫也確實沒有什么問題。 this.CheckRequiredAttribute(text3, "name", reader);
this.CheckRequiredAttribute(text4, "type", reader); this.VerifySectionName(text3, reader); string text5 = ConfigurationRecord.TagKey(configKey, text3); if (this.HaveFactory(text5) != ConfigurationRecord.HaveFactoryEnum.NotFound) { objArray1 = new object[] { text3 } ; throw this.BuildConfigError(SR.GetString("Tag_name_already_defined", objArray1), reader); } this.EnsureFactories[text5] = text4; goto Label_02B6; scansectionsrecursive方法會解析配置文件里的section實例,并將其tagkey儲存到hashtable _unevaluatedSections中,表示尚未evaluated的configkey的集合,可見section實例對象的創(chuàng)建和handler一樣,都是fetch when need。在后面的操作Getconfig中會使用他。 if (this._unevaluatedSections == null)
{ this._unevaluatedSections = new Hashtable(); } this._unevaluatedSections[text2] = null; 現(xiàn)在我們就可以看getconfig方法是怎么來執(zhí)行得到我們想要的對象的。 public object GetConfig(string configKey)
{ if (this._error != null) { throw this._error; } if (this._results.Contains(configKey)) { return this._results[configKey]; } object obj1 = this.ResolveConfig(configKey); lock (this._results.SyncRoot) { this._results[configKey] = obj1; } return obj1; } 如果_result中有對應(yīng)configkey的section實例,就返回,沒有則需要對configkey進行resolveconfig,將解析到的對象保存到_result中并返回給用戶。在resolveconfig方法中,可以看到如果當前的配置文件中沒有要求的configkey就會返回父級的section實例,比如machine.config里的內(nèi)容。 public object ResolveConfig(string configKey)
{ Hashtable hashtable1 = this._unevaluatedSections; if ((hashtable1 != null) && hashtable1.Contains(configKey)) { return this.Evaluate(configKey); } if (this._parent != null) { return this._parent.GetConfig(configKey); } return null; } 然后就是evaluate及其后續(xù)操作了,主要就是將configkey分解成字符串數(shù)組,一層層地去找對應(yīng)的xmlelement,找到后傳給configkey對應(yīng)的handler,如果該handler沒有創(chuàng)建則反射創(chuàng)建,然后由該handler創(chuàng)建section的實例對象,返回給用戶,該部分關(guān)鍵代碼如下: ConfigXmlDocument document1 = new ConfigXmlDocument();
document1.LoadSingleElement(this._filename, reader); config = factory.Create(config, null, document1.DocumentElement); 現(xiàn)在我們就明白了當我們用system..configurtion.configuationsetting.getconfig的時候發(fā)生過什么了。 作者: 來源: (責(zé)任編輯:webjx)
|
|
|