对于CS的分析你可以能会从页面开始,其实那并不是一个很好的方法,因为CS采用了MasterPage和内建的Theme与Skins,页面一层嵌套一层,如果你对CS页面执行机制不了解,或者你是初学者,这个时候可能就会碰壁,接着就放弃了对CS更深入的了解。我希望我的专题能从CS的运行过程开始一步一步地讲解,同时把ASP.NET的运行机理也表述出来,因此学习了解CS的过程就是对ASP.NET深入了解得过程。当然,我个人的开发经验与水平也是有限的,如果在专题中表述有问题,或者有疑问可以直接在文章的评论中直接指出,我将万分感谢你。
在分析CSHttpModule.cs的时候,你会看到这样两句代码:
CSEvents.UserKnown(csContext.User);
CSEvents.CSException(csException);
其实短短两行代码后面隐藏了Delegates与Events的大量运用,CS也通过这样的运用实现了一种模块化的处理机制,即CSModules。
打开CommunityServerWeb项目下的communityserver.config文件,这是CS的配置文件(与Web.config不同,communityserver.config主要配置的是CS内部的一些运行机制,而Web.config主要配置的是与Asp.net的交互)。找到文件中的这段:
我们拿出其中的一个来分析运行过程,例:
<add name = "CSExceptionModule" type = "CommunityServer.Components.CSExceptionModule, CommunityServer.Components" />
这是CS中异常处理的模块,当发生异常的时候该模块将调用一个RedirectToMessage方法,提示一个友好的错误界面,告诉请求的用户有错误发生。那么CS系统是如何在发生错误的时候自动调用RedirectToMessage方法转向另外一个页面提示友好错误的呢?先打开CommunityServerComponents项目下Components文件夹中的CSApplication.cs
文件太长,我们抓出关键的部分来分析:
这里先申明一个委托,相当于一个函数指针。在通俗一点理解它就是一个跑腿的,专管传递对象与对象间的调用信息。
接下来:
这段很重要,通过读取communityserver.config文件的<CSModules>,初始化每个CSModule,注意,初始化后并且调用了这些CSModule中的Init方法。具体看看这些Module中的Init都做了什么,打开CommunityServerComponents项目下的Components文件夹中的CSExceptionModule.cs:
哈哈,原来在Init方法里把一个CSExceptionHandler委托添加到CSException事件上,这个委托指向csa_CSException方法,还是通俗点说:如果CSException这个事件发生了,CSExceptionHandler这个跑腿的委托就会马上告诉csa_CSException方法要他执行,如果事件没有被激发就什么也不做。
名词: event 关键字使您得以指定当代码中的某些“事件”发生时调用的委托。此委托可以有一个或多个关联的方法,当代码指示该事件已发生时将调用关联的方法。
那么这个CSException又是怎么回事?在哪里定义的?我们回到CSApplication.cs文件中,看样几行:
这里定义了一个CSException事件,而事件发生的时候只能用CSExceptionHandler这个委托来做跑腿的。其实CS中是把委托都存放在了一个EventHandlerList中,因此此处你可以看到add与remove,
这是访问器的声明,用于添加或移除客户代码中的事件处理程序,这样做的好处是公开大量的事件但不为每个事件分配字段,而是使用EventHandlerList存储这些事件实例。为了理解事件的调用执行过程,我们还必须看几个文件:CSEvents.cs、CSEventArgs.cs:CSEventArgs.cs存储事件的数据,这个很好理解,它继承自EventArgs。当事件发生时CSEventArgs用来传递事件的信息,这里传递两个值:ObjectState与ApplicationType(可以在Enumerations文件夹下找到这两个枚举的内容)
CSEvents.cs这是对事件调用的一个包装器,看异常处理的包装:
这里先调用CSApplication.Instance()方法,实例化一个CSApplication对象,如果你是第一次调用Instance()方法,就实例化所有在<CSModules>中配置的类,并且调用他们的Init方法(在CSModules中配置的这些类,都实现了ICSModule接口,而这个接口要求继承他的类都具备Init方法),执行Init方法的目的就是把委托添加到事件上,使委托指向的方法可以在事件触发的时候被调用。实例化后再调用ExecuteCSExcetion方法并且传递CSException的实例,ExecuteCSExcetion方法如下:
先通过对EventHandlerList索引访问,即Events[EventUnhandledException],从列表中找到这个CSExceptionHandler事件,如果不为null就执行它。EventUnhandledException又是什么,其实这只是一个Key,用来标示存储的事件。
有必要总结一下,不然你会被这种调用来调用去的关系搞得一头雾水,
以异常处理为例:
1:在错误发生后,调用Application_OnError方法;
2:在方法的最后调用CSEvents.CSException(csException);
3:进入CSEvents包装器,调用CSApplication.Instance().ExecuteCSExcetion(csEx);
4:执行CSApplication.Instance()方法,如果是第一次执行就根据communityserver.config文件中的配置,把所有的CSModules实例化,并且调用ICSModule接口类中的Init方法,然后缓存这些实例化的类(如果是第二次访问就从缓存中读取)。
5:在实现ICSModule接口的类中,如CSExceptionModule.cs,Init方法是给事件添加委托的过程,这个过程中实现了委托指向的一个或者多个方法与事件进行关联,异常处理的方法csa_CSException(CSException csEx, CSEventArgs e)就是在这里被关联到异常事件上的。
6:经过上面几步后,CS系统接着调用ExecuteCSExcetion方法,在ExecuteCSExcetion方触发了CSException事件
7:CSException事件被触发后,就执行事件中委托所指向的函数,这里是CSExceptionModule.cs文件中的private void csa_CSException(CSException csEx, CSEventArgs e)。
CS如此大量的使用Delegates与Events带来了什么,也许你会认为它这样是把问题复杂化,而且觉得这非常没有必要,完全可以在异常处理的最后调用处理方法即可,何必通过事件来回周转!最后说明一下这样做的重要性:
1:通过事件使调用方法者与方法本身隔离,如在CSHttpModule.cs文件中的Application_OnError方法触发CSEvents.CSException事件,而事件要做些什么处理,需要调用什么方法Application_OnError根本不知道。如果你要改变CSEvents.CSException事件处理方法的结构,甚至十处理方法的名称,Application_OnError也不需要改动,因为他根本不关心具体的实现,它的任务只是触发这个事件。
2:如果你想一个方法调用多个方法,普通的做法就是在方法中一次调用或者在方法中嵌套调用。这样做并不是一个好的设计模式,而事件可以通过委托调用多个委托指向的方法(在异常处理中只指向了一个方法,当然你可以指向任意N个方法),而这种调用也是相互隔离的,被调用的方法并不致到谁调用它,而调用者也不关心它调用谁。
3:模块化,你的代码直接没有非常的多紧密联系,而是通过事件来通知处理方法,在CS中又加入了xml的配置文件,使得这样的模块化更突出,你甚至可以把处理异常的类单独编译在一个dll中。
好处还有很多…
在CS中,对Post内容的不良信息过滤也是通过这样的机制完成的,运行的过程基本一致,只是调用不同的事件处理方法,你可以自己分析。