对于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的交互)。找到文件中的这段:
<csmodules>
<add name = "csmembershiprulesmodule" type = "communityserver.components.csmembershiprulesmodule, communityserver.components" />
<add name = "cscatastrophicexceptionmodule" type = "communityserver.components.cscatastrophicexceptionmodule, communityserver.components" />
<add name = "csexceptionmodule" type = "communityserver.components.csexceptionmodule, communityserver.components" />
<add name = "irccommands" type = "communityserver.discussions.components.irccommandsmodule, communityserver.discussions" />
<add name = "forumcensorship" type = "communityserver.discussions.components.censorshipmodule, communityserver.discussions" />
<add name = "forumemoticon" type = "communityserver.discussions.components.emoticonmodule, communityserver.discussions" />
<add name = "forumsourcecode" type = "communityserver.discussions.components.sourcecodemodule, communityserver.discussions" />
<add name = "forumhtmlscrubbing" type = "communityserver.discussions.components.htmlscrubbingmodule, communityserver.discussions" />
<add name = "bbcodetohtml" type = "communityserver.discussions.components.bbcodetohtmlmodule, communityserver.discussions" />
<add name = "forumplaintext" type = "communityserver.discussions.components.plaintextmodule, communityserver.discussions" />
<add name = "weblogcensormodule" type = "communityserver.blogs.components.censormodule, communityserver.blogs" />
<add name = "weblogpostandarticlehtmlscrubbing" type = "communityserver.blogs.components.postandarticlehtmlscrubbing, communityserver.blogs" />
<add name = "weblogfeedbackhtmlformatting" type = "communityserver.blogs.components.feedbackhtmlformatting, communityserver.blogs" />
<add name = "trackbackmodule" type = "communityserver.blogs.components.trackbackmodule, communityserver.blogs" />
<add name = "xmlrpcpingmodule" type = "communityserver.blogs.components.xmlrpcpingmodule, communityserver.blogs" />
<add name = "weblogformattingmodule" type = "communityserver.blogs.components.weblogformattingmodule, communityserver.blogs" />
<add name = "picturecensor" type = "communityserver.galleries.components.censorpicturemodule, communityserver.galleries" />
<add name = "picturehtmlscrubber" type = "communityserver.galleries.components.htmlscrubbermodule, communityserver.galleries" />
<add name = "picturecomments" type = "communityserver.galleries.components.commentmodule, communityserver.galleries" />
<!-- <add name = "maxpicturesize" type = "communityserver.galleries.components.maxpicturesizemodule, communityserver.galleries" maxwidth="1024" maxheight="768" quality="90" /> -->
</csmodules>
我们拿出其中的一个来分析运行过程,例:
<add name = "csexceptionmodule" type = "communityserver.components.csexceptionmodule, communityserver.components" />
这是cs中异常处理的模块,当发生异常的时候该模块将调用一个redirecttomessage方法,提示一个友好的错误界面,告诉请求的用户有错误发生。那么cs系统是如何在发生错误的时候自动调用redirecttomessage方法转向另外一个页面提示友好错误的呢?先打开communityservercomponents项目下components文件夹中的csapplication.cs
using system;
using system.collections;
using system.componentmodel;
using system.web.caching;
using system.xml;
using communityserver.configuration;
namespace communityserver.components
{
delegates#region delegates
//do we want one single delegate or a custom one for each type
//public delegate void cseventhandler(object sender, cseventargs e);
public delegate void csusereventhandler(user user, cseventargs e);
public delegate void csposteventhandler(post post, cseventargs e);
public delegate void cssectioneventhandler(section section, cseventargs e);
public delegate void csgroupeventhandler(group group, cseventargs e);
public delegate void csexceptionhandler(csexception csex, cseventargs e);
#endregion
/**//// <summary>
/// summary description for csapplication.
/// </summary>
public class csapplication
{
private members#region private members
private eventhandlerlist events = new eventhandlerlist();
private static readonly object sync = new object();
private hashtable modules = new hashtable();
#endregion
event keys (static)#region event keys (static)
private static object eventauthorizepost = new object();
private static object eventprepostupdate = new object();
private static object eventpreprocesspost = new object();
private static object eventpostpostupdate = new object();
private static object eventratepost = new object();
//private static object eventprerenderpost = new object();
private static object eventpreuserupdate = new object();
private static object eventpostuserupdate = new object();
private static object eventuserremove = new object();
private static object eventuserknown = new object();
private static object eventuservalidated = new object();
private static object eventpresectionupdate = new object();
private static object eventpostsectionupdate = new object();
private static object eventpresectiongroupupdate = new object();
private static object eventpostsectiongroupupdate = new object();
private static object eventunhandledexception = new object();
#endregion
cnstr#region cnstr
private csapplication()
{
}
internal static csapplication instance()
{
const string key = "csapplication";
csapplication app = cscache.get(key) as csapplication;
if(app == null)
{
lock(sync)
{
app = cscache.get(key) as csapplication;
if(app == null)
{
csconfiguration config = cscontext.current.config;
xmlnode node = config.getconfigsection("communityserver/csmodules");
app = new csapplication();
if(node != null)
{
foreach(xmlnode n in node.childnodes)
{
if(n.nodetype != xmlnodetype.comment)
{
switch(n.name)
{
case "clear":
app.modules.clear();
break;
case "remove":
app.modules.remove(n.attributes["name"].value);
break;
case "add":
string name = n.attributes["name"].value;
string itype = n.attributes["type"].value;
type type = type.gettype(itype);
if(type == null)
throw new exception(itype + " does not exist");
icsmodule mod = activator.createinstance(type) as icsmodule;
if(mod == null)
throw new exception(itype + " does not implement icsmodule or is not configured correctly");
mod.init(app, n);
app.modules.add(name,mod);
break;
}
}
}
}
cachedependency dep = new cachedependency(null, new string[]{csconfiguration.cachekey});
cscache.max(key, app,dep);
}
}
}
return app;
}
#endregion
post events#region post events
execute events#region execute events
internal void executeauthorizepost()
{
executeuserevent(eventauthorizepost,cscontext.current.user);
}
internal void executeprepostevents(post post, objectstate state, applicationtype apptype)
{
executepostevent(eventpreprocesspost,post,state,apptype);
}
internal void executeprepostupdateevents(post post, objectstate state, applicationtype apptype)
{
executepostevent(eventprepostupdate,post,state,apptype);
}
internal void executepostpostupdateevents(post post, objectstate state, applicationtype apptype)
{
executepostevent(eventpostpostupdate,post,state,apptype);
}
internal void executeratepostevents(post post, applicationtype apptype)
{
executepostevent(eventratepost,post,objectstate.none,apptype);
}
// internal void executeprepostrender(post post, applicationtype apptype)
// {
// executepostevent(eventprerenderpost,post,objectstate.none,apptype);
// }
protected void executepostevent(object eventkey, post post,objectstate state, applicationtype apptype)
{
csposteventhandler handler = events[eventkey] as csposteventhandler;
if (handler != null)
{
handler(post, new cseventargs(state,apptype));
}
}
#endregion
events#region events
/**//// <summary>
/// event raised before a user accesses a page which can be used to create content
/// </summary>
public event csusereventhandler authorizepost
{
add{events.addhandler(eventauthorizepost, value);}
remove{events.removehandler(eventauthorizepost, value);}
}
/**//// <summary>
/// event raised before any post processing takes place
/// </summary>
public event csposteventhandler preprocesspost
{
add{events.addhandler(eventpreprocesspost, value);}
remove{events.removehandler(eventpreprocesspost, value);}
}
/**//// <summary>
/// fires after preprocesspost but before the post change is commited to the datastore
/// </summary>
public event csposteventhandler prepostupdate
{
add{events.addhandler(eventprepostupdate, value);}
remove{events.removehandler(eventprepostupdate, value);}
}
/**//// <summary>
/// fires after a post change is commited to the datastore
/// </summary>
public event csposteventhandler postpostupdate
{
add{events.addhandler(eventpostpostupdate, value);}
remove{events.removehandler(eventpostpostupdate, value);}
}
/**//// <summary>
/// fires after a post or thread is rated
/// </summary>
public event csposteventhandler ratepost
{
add{events.addhandler(eventratepost, value);}
remove{events.removehandler(eventratepost, value);}
}
// /// <summary>
// /// event raised before an individual post is rendered
// /// </summary>
// public event csposteventhandler prerenderpost
// {
// add{events.addhandler(eventprerenderpost, value);}
// remove{events.removehandler(eventprerenderpost, value);}
// }
#endregion
#endregion
user events#region user events
execute events#region execute events
internal void executeuservalidated(user user)
{
executeuserevent(eventuservalidated,user);
}
internal void executeuserknown(user user)
{
executeuserevent(eventuserknown,user);
}
internal void executepreuserupdate(user user, objectstate state)
{
executeuserevent(eventpreuserupdate,user,state,applicationtype.unknown);
}
internal void executepostuserupdate(user user, objectstate state)
{
executeuserevent(eventpostuserupdate,user,state,applicationtype.unknown);
}
internal void executeuserremove(user user)
{
executeuserevent(eventuserremove,user,objectstate.delete,applicationtype.unknown);
}
protected void executeuserevent(object eventkey, user user)
{
executeuserevent(eventkey,user,objectstate.none,applicationtype.unknown);
}
protected void executeuserevent(object eventkey, user user,objectstate state, applicationtype apptype)
{
csusereventhandler handler = events[eventkey] as csusereventhandler;
if (handler != null)
{
handler(user, new cseventargs(state,apptype));
}
}
#endregion
events#region events
/**//// <summary>
/// fires after a user's credentials have been validated.
/// </summary>
public event csusereventhandler uservalidated
{
add{events.addhandler(eventuservalidated, value);}
remove{events.removehandler(eventuservalidated, value);}
}
/**//// <summary>
/// fires once the current user has been identified. this user may still be anonymous.
/// </summary>
public event csusereventhandler userknown
{
add{events.addhandler(eventuserknown, value);}
remove{events.removehandler(eventuserknown, value);}
}
/**//// <summary>
/// fires before a user is saved/updated to the datastore
/// </summary>
public event csusereventhandler preuserupdate
{
add{events.addhandler(eventpreuserupdate, value);}
remove{events.removehandler(eventpreuserupdate, value);}
}
/**//// <summary>
/// fires after a user is saved/updated to the datastore
/// </summary>
public event csusereventhandler postuserupdate
{
add{events.addhandler(eventpostuserupdate, value);}
remove{events.removehandler(eventpostuserupdate, value);}
}
/**//// <summary>
/// fires before a user is removed from the datastore.
/// </summary>
public event csusereventhandler userremove
{
add{events.addhandler(eventuserremove, value);}
remove{events.removehandler(eventuserremove, value);}
}
#endregion
#endregion
section events#region section events
internal void executepresectionupdate(section section, objectstate state, applicationtype apptype)
{
cssectioneventhandler handler = events[eventpresectionupdate] as cssectioneventhandler;
if (handler != null)
{
handler(section, new cseventargs(state,apptype));
}
}
internal void executepostsectionupdate(section section, objectstate state, applicationtype apptype)
{
cssectioneventhandler handler = events[eventpostsectionupdate] as cssectioneventhandler;
if (handler != null)
{
handler(section, new cseventargs(state,apptype));
}
}
/**//// <summary>
/// event raised before a section change is committed to the datastore (create/update)
/// </summary>
public event cssectioneventhandler presectionupdate
{
add{events.addhandler(eventpresectionupdate, value);}
remove{events.removehandler(eventpresectionupdate, value);}
}
/**//// <summary>
/// event raised after a section chage is committed to the data store
/// </summary>
public event cssectioneventhandler postsectionupdate
{
add{events.addhandler(eventpostsectionupdate, value);}
remove{events.removehandler(eventpostsectionupdate, value);}
}
#endregion
group events#region group events
internal void executepresectiongroupupdate(group group, objectstate state, applicationtype apptype)
{
csgroupeventhandler handler = events[eventpresectiongroupupdate] as csgroupeventhandler;
if (handler != null)
{
handler(group, new cseventargs(state,apptype));
}
}
internal void executepostsectiongroupupdate(group group, objectstate state, applicationtype apptype)
{
csgroupeventhandler handler = events[eventpostsectiongroupupdate] as csgroupeventhandler;
if (handler != null)
{
handler(group, new cseventargs(state,apptype));
}
}
/**//// <summary>
/// event raised before a group chage is committed to the datastore (create/update)
/// </summary>
public event csgroupeventhandler presectiongroupupdate
{
add{events.addhandler(eventpresectiongroupupdate, value);}
remove{events.removehandler(eventpresectiongroupupdate, value);}
}
/**//// <summary>
/// event raised after a group chage is committed to the data store
/// </summary>
public event csgroupeventhandler postsectiongroupupdate
{
add{events.addhandler(eventpostsectiongroupupdate, value);}
remove{events.removehandler(eventpostsectiongroupupdate, value);}
}
#endregion
exceptions#region exceptions
/**//// <summary>
/// event raised before a group chage is committed to the datastore (create/update)
/// </summary>
public event csexceptionhandler csexception
{
add{events.addhandler(eventunhandledexception, value);}
remove{events.removehandler(eventunhandledexception, value);}
}
internal void executecsexcetion(csexception csex)
{
csexceptionhandler handler = events[eventunhandledexception] as csexceptionhandler;
if (handler != null)
{
handler(csex,new cseventargs());
}
}
#endregion
}
}
文件太长,我们抓出关键的部分来分析:
public delegate void csexceptionhandler(csexception csex, cseventargs e);
这里先申明一个委托,相当于一个函数指针。在通俗一点理解它就是一个跑腿的,专管传递对象与对象间的调用信息。
接下来:
internal static csapplication instance()
{
const string key = "csapplication";
csapplication app = cscache.get(key) as csapplication;
if(app == null)
{
lock(sync)
{
app = cscache.get(key) as csapplication;
if(app == null)
{
csconfiguration config = cscontext.current.config;
xmlnode node = config.getconfigsection("communityserver/csmodules");
app = new csapplication();
if(node != null)
{
foreach(xmlnode n in node.childnodes)
{
if(n.nodetype != xmlnodetype.comment)
{
switch(n.name)
{
case "clear":
app.modules.clear();
break;
case "remove":
app.modules.remove(n.attributes["name"].value);
break;
case "add":
string name = n.attributes["name"].value;
string itype = n.attributes["type"].value;
type type = type.gettype(itype);
if(type == null)
throw new exception(itype + " does not exist");
icsmodule mod = activator.createinstance(type) as icsmodule;
if(mod == null)
throw new exception(itype + " does not implement icsmodule or is not configured correctly");
mod.init(app, n);
app.modules.add(name,mod);
break;
}
}
}
}
cachedependency dep = new cachedependency(null, new string[]{csconfiguration.cachekey});
cscache.max(key, app,dep);
}
}
}
return app;
}
这段很重要,通过读取communityserver.config文件的<csmodules>,初始化每个csmodule,注意,初始化后并且调用了这些csmodule中的init方法。具体看看这些module中的init都做了什么,打开communityservercomponents项目下的components文件夹中的csexceptionmodule.cs:
using system;
using system.web;
namespace communityserver.components
{
/**//// <summary>
/// summary description for csexceptionmodule.
/// </summary>
public class csexceptionmodule : icsmodule
{
public csexceptionmodule()
{
//
// todo: add constructor logic here
//
}
icsmodule members#region icsmodule members
public void init(csapplication csa, system.xml.xmlnode node)
{
csa.csexception +=new csexceptionhandler(csa_csexception);
}
#endregion
private void csa_csexception(csexception csex, cseventargs e)
{
cscontext cscontext = cscontext.current;
if (csex.exceptiontype != csexceptiontype.unknownerror && cscontext.iswebrequest)
{
redirecttomessage(cscontext.context, csex);
}
}
private static void redirecttomessage (httpcontext context, csexception exception)
{
if ((exception.innerexception != null) && ( exception.innerexception is csexception))
{
csexception inner = (csexception) exception.innerexception;
}
context.response.redirect(globals.getsiteurls().message( exception.exceptiontype ), true);
}
}
}
哈哈,原来在init方法里把一个csexceptionhandler委托添加到csexception事件上,这个委托指向csa_csexception方法,还是通俗点说:如果csexception这个事件发生了,csexceptionhandler这个跑腿的委托就会马上告诉csa_csexception方法要他执行,如果事件没有被激发就什么也不做。
名词: event 关键字使您得以指定当代码中的某些“事件”发生时调用的委托。此委托可以有一个或多个关联的方法,当代码指示该事件已发生时将调用关联的方法。
那么这个csexception又是怎么回事?在哪里定义的?我们回到csapplication.cs文件中,看样几行:
public event csexceptionhandler csexception
{
add{events.addhandler(eventunhandledexception, value);}
remove{events.removehandler(eventunhandledexception, value);}
}
这里定义了一个csexception事件,而事件发生的时候只能用csexceptionhandler这个委托来做跑腿的。其实cs中是把委托都存放在了一个eventhandlerlist中,因此此处你可以看到add与remove, 这是访问器的声明,用于添加或移除客户代码中的事件处理程序,这样做的好处是公开大量的事件但不为每个事件分配字段,而是使用eventhandlerlist存储这些事件实例。为了理解事件的调用执行过程,我们还必须看几个文件:csevents.cs、cseventargs.cs:
cseventargs.cs存储事件的数据,这个很好理解,它继承自eventargs。当事件发生时cseventargs用来传递事件的信息,这里传递两个值:objectstate与applicationtype(可以在enumerations文件夹下找到这两个枚举的内容)
csevents.cs这是对事件调用的一个包装器,看异常处理的包装:
public static void csexception(csexception csex)
{
csapplication.instance().executecsexcetion(csex);
}
这里先调用csapplication.instance()方法,实例化一个csapplication对象,如果你是第一次调用instance()方法,就实例化所有在<csmodules>中配置的类,并且调用他们的init方法(在csmodules中配置的这些类,都实现了icsmodule接口,而这个接口要求继承他的类都具备init方法),执行init方法的目的就是把委托添加到事件上,使委托指向的方法可以在事件触发的时候被调用。实例化后再调用executecsexcetion方法并且传递csexception的实例,executecsexcetion方法如下:
internal void executecsexcetion(csexception csex)
{
csexceptionhandler handler = events[eventunhandledexception] as csexceptionhandler;
if (handler != null)
{
handler(csex,new cseventargs());
}
}
先通过对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:模块化,你?
新闻热点
疑难解答