欢迎光临
我们一直在努力

.NET配置文件解析过程详解-.NET教程,Asp.Net开发

建站超值云服务器,限时71元/月

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

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

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

 

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

<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框架内是如何利用这种结构的。反编译system.dll找到getconfig方法,在里面我们发现实际获取config的工作默认是由实现了iconfigurationsystemdefaultconfiguationsystem类来实现的。

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来处理,这个类没有实现任何接口,可见他和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里实现的,作为配置文件分析的第一步,正如我们经常做的一样->加载一个配置文件,这个方法公开为 load(filename)。defaultconfiguationsystem的init()方法中用machine.config创建了一个configuationrecord对象,并将其作为父对象传递给当前程序的configuationrecord对象,当然前提是当前程序有配置文件,比如myapp.config,然后再加载当前程序的配置文件,从而实现配置文件的继承。

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());
            }

      }

}

 

现在我们可以专注于configuationrecord的具体实现了,load方法中得到一个xmltextwriter,并执行.scanfactoriesrecursive和scansectionsrecursive方法。

 reader1 = configurationrecord.openxmltextreader(filename);
            
if (reader1 != null)
            
{
                  
this.scanfactoriesrecursive(reader1);
                  
if (reader1.depth == 1)
                  
{
                        
this.scansectionsrecursive(reader1, null);
                  }

                  
return true;
            }

 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语句,可读性不是太好,但这样写也确实没有什么问题。

   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实例对象的创建和handler一样,都是fetch when need。在后面的操作getconfig中会使用他。

    if (this._unevaluatedsections == null)
            
{
                  
this._unevaluatedsections = new hashtable();
            }

            
this._unevaluatedsections[text2] = null;

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

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

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

  configxmldocument document1 = new configxmldocument();
  document1.loadsingleelement(
this._filename, reader);
 config 
= factory.create(config, null, document1.documentelement);

 现在我们就明白了当我们用system..configurtion.configuationsetting.getconfig的时候发生过什么了。

赞(0)
版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com 特别注意:本站所有转载文章言论不代表本站观点! 本站所提供的图片等素材,版权归原作者所有,如需使用,请与原作者联系。未经允许不得转载:IDC资讯中心 » .NET配置文件解析过程详解-.NET教程,Asp.Net开发
分享到: 更多 (0)