.NET配置文件解析过程详解

本文详细介绍了.NET配置文件的解析过程。指出ASP.NET框架下开发与WINFORM开发的区别,强调从配置文件学习asp.net框架。阐述了.net配置文件的特点,通过反编译System.dll分析其运行方式,包括ScanFactoriesRecursive和ScanSectionsRecursive方法的解析过程,以及Getconfig方法的执行逻辑。
 

.NET配置文件解析过程详解

在我看来,WEB project的开发与WINFORM的开发最大的区别在于web的运行是在Framework上更高一层框架上运行,即ASPNET框架,程序员在web下的开发可以说是黑盒开发,不是让你去定义程序入口和执行顺序,而是asp.net来调用你的各个方法,程序员做的一切都是一种受控的舞蹈。就像我们调用nunit之类的工具来测试一个dll一样,nunit是容器,是框架,执行哪个方法是由nunt来决定的。因此,也就有了页面执行周期各状态等令刚入门的程序员困惑不已的事,其实,究其根源,在于不了解容器而去使用容器。对于asp.net框架的学习,我们不妨从配置文件开始。

对于程序开发者而言,写配置文件是经常性的工作,如果你写了一个xx.config文件,如果没有详尽的注释,别人恐怕很难读懂,没有良好的配置架构,程序也失去了活力。在我看来,.net配置文件的特点在于反射定义和继承性。

我们访问配置文件时经常觉得配置文件的结构不太符合我们的需要,我们需要从里面更方便地获得自己定义的对象,而不仅仅是keyvalue,对于自定义配置文件的著述已有很多,在此不再描述,有兴趣的朋友可以访问http://ly4cn.cnblogs.com/archive/2005/09/06/231245.html

自定义配置节其实还是在.net配置文件架构的应用而已,我们先来搞懂配置文件的结构,弄清楚.net配置文件的运行方式。下面是machine.config的一部分内容:

None.gif < configSections >
None.gif     
< section  name ="runtime"   type ="System.Configuration.IgnoreSectionHandler, System, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"  allowLocation ="false"   />
None.gif
< sectionGroup  name ="system.net" >
None.gif            
< section  name ="authenticationModules"  type ="System.Net.Configuration.NetAuthenticationModuleHandler, System, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"   />
None.gif
</  sectionGroup >
None.gif
</ configSections >
None.gif

  SDK<section>的定义为:

None.gif < section
None.gif   
name ="section name"
None.gif   type
="configuration section handler class, assembly"
None.gif   allowDefinition
="Everywhere|MachineOnly|MachineToApplication"  
None.gif   allowLocation
="true|false"   />
None.gif

<sectionGroup>的定义为:

None.gif < sectionGroup
None.gif   
name ="section group name" />
None.gif
</ sectionGroup >
None.gif

  我们来看看.net框架内是如何利用这种结构的。反编译System.dll找到GetConfig方法,在里面我们发现实际获取config的工作默认是由实现了IConfigurationSystemDefaultConfiguationSystem类来实现的。

None.gif public   static   object  GetConfig( string  sectionName)
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif      
if (!ConfigurationSettings._configurationInitialized)
ExpandedSubBlockStart.gifContractedSubBlock.gif      
dot.gif{
InBlock.gif            
lock (typeof(ConfigurationSettings))
ExpandedSubBlockStart.gifContractedSubBlock.gif            
dot.gif{
InBlock.gif                  
if ((ConfigurationSettings._configSystem == null&& !ConfigurationSettings.SetConfigurationSystemInProgress)
ExpandedSubBlockStart.gifContractedSubBlock.gif                  
dot.gif{
InBlock.gif                        ConfigurationSettings.SetConfigurationSystem(
new DefaultConfigurationSystem());
ExpandedSubBlockEnd.gif                  }

ExpandedSubBlockEnd.gif            }

ExpandedSubBlockEnd.gif      }

InBlock.gif      
if (ConfigurationSettings._initError != null)
ExpandedSubBlockStart.gifContractedSubBlock.gif      
dot.gif{
InBlock.gif            
throw ConfigurationSettings._initError;
ExpandedSubBlockEnd.gif      }

InBlock.gif      
return ConfigurationSettings._configSystem.GetConfig(sectionName);
ExpandedBlockEnd.gif}

None.gif

  我们再来看 DefaultConfigurationSystem ,这个类主要包含了 machine.config 的名称路径的基本信息和一些 uri 操作,而实际的 GetConfig 的操作交给了 ConfiguationRecord 来处理,这个类没有实现任何接口,可见他和 DefaultConfiguration 是绑定在一起的。
 1 None.gif internal   class  DefaultConfigurationSystem : IConfigurationSystem
 2 ExpandedBlockStart.gifContractedBlock.gif dot.gif {
 3InBlock.gif      // Methods
 4InBlock.gif      internal DefaultConfigurationSystem();
 5InBlock.gif      object IConfigurationSystem.GetConfig(string configKey);
 6InBlock.gif      void IConfigurationSystem.Init();
 7InBlock.gif
 8InBlock.gif      // Properties
 9ExpandedSubBlockStart.gifContractedSubBlock.gif      internal static Uri AppConfigPath dot.gifget; }
10ExpandedSubBlockStart.gifContractedSubBlock.gif      internal static string MachineConfigurationFilePath dot.gifget; }
11ExpandedSubBlockStart.gifContractedSubBlock.gif      internal static string MsCorLibDirectory dot.gifget; }
12InBlock.gif
13InBlock.gif      // Fields
14InBlock.gif      private ConfigurationRecord _application;
15InBlock.gif      private const string ConfigExtension = "config";
16InBlock.gif      private const string MachineConfigFilename = "machine.config";
17InBlock.gif      private const string MachineConfigSubdirectory = "Config";
18InBlock.gif      private const int MaxPathSize = 0x400;
19ExpandedBlockEnd.gif}

20 None.gif

事实上所有的配置文件的分析和获取都是在ConfiguationRecord里实现的,作为配置文件分析的第一步,正如我们经常做的一样->加载一个配置文件,这个方法公开为 Load(filename)。DefaultConfiguationSystem的Init()方法中用machine.config创建了一个ConfiguationRecord对象,并将其作为父对象传递给当前程序的ConfiguationRecord对象,当然前提是当前程序有配置文件,比如myapp.config,然后再加载当前程序的配置文件,从而实现配置文件的继承。
None.gif void  IConfigurationSystem.Init()
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif      
lock (this)
ExpandedSubBlockStart.gifContractedSubBlock.gif      
dot.gif{
InBlock.gif            
if (this._application == null)
ExpandedSubBlockStart.gifContractedSubBlock.gif            
dot.gif{
InBlock.gif                  ConfigurationRecord record1 
= null;
InBlock.gif                  
string text1 = DefaultConfigurationSystem.MachineConfigurationFilePath;
InBlock.gif                  Uri uri1 
= DefaultConfigurationSystem.AppConfigPath;
InBlock.gif                  
this._application = record1 = new ConfigurationRecord();
InBlock.gif                  
bool flag1 = record1.Load(text1);
InBlock.gif                  
if (!flag1 || (uri1 == null))
ExpandedSubBlockStart.gifContractedSubBlock.gif                  
dot.gif{
InBlock.gif                        
return;
ExpandedSubBlockEnd.gif                  }

InBlock.gif                  
this._application = new ConfigurationRecord(record1);
InBlock.gif                  
this._application.Load(uri1.ToString());
ExpandedSubBlockEnd.gif            }

ExpandedSubBlockEnd.gif      }

ExpandedBlockEnd.gif}

None.gif 
None.gif

现在我们可以专注于ConfiguationRecord的具体实现了,Load方法中得到一个XmlTextWriter,并执行.ScanFactoriesRecursive和ScanSectionsRecursive方法。

None.gif  reader1  =  ConfigurationRecord.OpenXmlTextReader(filename);
None.gif            
if  (reader1  !=   null )
ExpandedBlockStart.gifContractedBlock.gif            
dot.gif {
InBlock.gif                  
this.ScanFactoriesRecursive(reader1);
InBlock.gif                  
if (reader1.Depth == 1)
ExpandedSubBlockStart.gifContractedSubBlock.gif                  
dot.gif{
InBlock.gif                        
this.ScanSectionsRecursive(reader1, null);
ExpandedSubBlockEnd.gif                  }

InBlock.gif                  
return true;
ExpandedBlockEnd.gif            }

None.gif

 ScanFactoriesRecursive方法会调用他的一个重载方法来解析<configSections>中的<sectionGroup>,<section>,<remove>,<clear>,我们写配置文件时大小写可不能写错哦,.NET没有做toslower之类的转换,直接就是 “== “。在这个方法里程序会将解析得到的sectiongroup以key=tagkey,value= ConfigurationRecord.GroupSingleton的方式存到EnsureFactories里,将section以key=tagkey,value=typestring的方式储存,值得注意的是,这里并没有创建实现IConfigurationSectionHandler的实例对象,而是将其类型名(比如:字符串”system.Configuration.NameValueSectionHandler”)作为值到EnsureFactories,等到后面GetConfig的时候再来反射创建。<remove>则存为ConfigurationRecord.RemovedFactorySingleton。<clear>就清空EnsureFactories。这里的tagkey是各级name的组合,比如”mygroup/mysection”这样以分隔符”/”组合的形式。应该客观地说这部分代码用了很多goto语句,可读性不是太好,但这样写也确实没有什么问题。

None.gif     this .CheckRequiredAttribute(text3,  " name " , reader);
None.gif            
this .CheckRequiredAttribute(text4,  " type " , reader);
None.gif            
this .VerifySectionName(text3, reader);
None.gif            
string  text5  =  ConfigurationRecord.TagKey(configKey, text3);
None.gif            
if  ( this .HaveFactory(text5)  !=  ConfigurationRecord.HaveFactoryEnum.NotFound)
ExpandedBlockStart.gifContractedBlock.gif            
dot.gif {
ExpandedSubBlockStart.gifContractedSubBlock.gif                  objArray1 
= new object[] dot.gif{ text3 } ;
InBlock.gif                  
throw this.BuildConfigError(SR.GetString("Tag_name_already_defined", objArray1), reader);
ExpandedBlockEnd.gif            }

None.gif            
this .EnsureFactories[text5]  =  text4;
None.gif            
goto  Label_02B6;
None.gif

ScanSectionsRecursive方法会解析配置文件里的section实例,并将其tagkey储存到Hashtable _unevaluatedSections中,表示尚未evaluated的configkey的集合,可见section实例对象的创建和handler一样,都是fetch when need。在后面的操作Getconfig中会使用他。

None.gif      if  ( this ._unevaluatedSections  ==   null )
ExpandedBlockStart.gifContractedBlock.gif            
dot.gif {
InBlock.gif                  
this._unevaluatedSections = new Hashtable();
ExpandedBlockEnd.gif            }

None.gif            
this ._unevaluatedSections[text2]  =   null ;
None.gif

现在我们就可以看Getconfig方法是怎么来执行得到我们想要的对象的。

None.gif public   object  GetConfig( string  configKey)
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif      
if (this._error != null)
ExpandedSubBlockStart.gifContractedSubBlock.gif      
dot.gif{
InBlock.gif            
throw this._error;
ExpandedSubBlockEnd.gif      }

InBlock.gif      
if (this._results.Contains(configKey))
ExpandedSubBlockStart.gifContractedSubBlock.gif      
dot.gif{
InBlock.gif            
return this._results[configKey];
ExpandedSubBlockEnd.gif      }

InBlock.gif      
object obj1 = this.ResolveConfig(configKey);
InBlock.gif      
lock (this._results.SyncRoot)
ExpandedSubBlockStart.gifContractedSubBlock.gif      
dot.gif{
InBlock.gif            
this._results[configKey] = obj1;
ExpandedSubBlockEnd.gif      }

InBlock.gif      
return obj1;
ExpandedBlockEnd.gif}

None.gif

如果_result中有对应configkey的section实例,就返回,没有则需要对configkey进行ResolveConfig,将解析到的对象保存到_result中并返回给用户。在ResolveConfig方法中,可以看到如果当前的配置文件中没有要求的configkey就会返回父级的section实例,比如machine.config里的内容。

None.gif public   object  ResolveConfig( string  configKey)
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif      Hashtable hashtable1 
= this._unevaluatedSections;
InBlock.gif      
if ((hashtable1 != null&& hashtable1.Contains(configKey))
ExpandedSubBlockStart.gifContractedSubBlock.gif      
dot.gif{
InBlock.gif            
return this.Evaluate(configKey);
ExpandedSubBlockEnd.gif      }

InBlock.gif      
if (this._parent != null)
ExpandedSubBlockStart.gifContractedSubBlock.gif      
dot.gif{
InBlock.gif            
return this._parent.GetConfig(configKey);
ExpandedSubBlockEnd.gif      }

InBlock.gif      
return null;
ExpandedBlockEnd.gif}

None.gif 
None.gif

然后就是Evaluate及其后续操作了,主要就是将configkey分解成字符串数组,一层层地去找对应的xmlelement,找到后传给configkey对应的handler,如果该handler没有创建则反射创建,然后由该handler创建section的实例对象,返回给用户,该部分关键代码如下:

None.gif   ConfigXmlDocument document1  =   new  ConfigXmlDocument();
None.gif  document1.LoadSingleElement(
this ._filename, reader);
None.gif config 
=  factory.Create(config,  null , document1.DocumentElement);
None.gif

 现在我们就明白了当我们用System..Configurtion.ConfiguationSetting.GetConfig的时候发生过什么了。
  
  在下一篇文章中,我会对系统内置的具体section的handler的实现做详细的阐述。

转载于:https://www.cnblogs.com/sharpedge/archive/2005/12/01/288276.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值