對(duì)于cs的分析你可以能會(huì)從頁(yè)面開(kāi)始,其實(shí)那并不是一個(gè)很好的方法,因?yàn)閏s采用了masterpage和內(nèi)建的theme與skins,頁(yè)面一層嵌套一層,如果你對(duì)cs頁(yè)面執(zhí)行機(jī)制不了解,或者你是初學(xué)者,這個(gè)時(shí)候可能就會(huì)碰壁,接著就放棄了對(duì)cs更深入的了解。我希望我的專題能從cs的運(yùn)行過(guò)程開(kāi)始一步一步地講解,同時(shí)把a(bǔ)sp.net的運(yùn)行機(jī)理也表述出來(lái),因此學(xué)習(xí)了解cs的過(guò)程就是對(duì)asp.net深入了解得過(guò)程。當(dāng)然,我個(gè)人的開(kāi)發(fā)經(jīng)驗(yàn)與水平也是有限的,如果在專題中表述有問(wèn)題,或者有疑問(wèn)可以直接在文章的評(píng)論中直接指出,我將萬(wàn)分感謝你。
在分析cshttpmodule.cs的時(shí)候,你會(huì)看到這樣兩句代碼:
csevents.userknown(cscontext.user);
csevents.csexception(csexception);
其實(shí)短短兩行代碼后面隱藏了delegates與events的大量運(yùn)用,cs也通過(guò)這樣的運(yùn)用實(shí)現(xiàn)了一種模塊化的處理機(jī)制,即csmodules。
打開(kāi)communityserverweb項(xiàng)目下的communityserver.config文件,這是cs的配置文件(與web.config不同,communityserver.config主要配置的是cs內(nèi)部的一些運(yùn)行機(jī)制,而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>
我們拿出其中的一個(gè)來(lái)分析運(yùn)行過(guò)程,例:
<add name = "csexceptionmodule" type = "communityserver.components.csexceptionmodule, communityserver.components" />
這是cs中異常處理的模塊,當(dāng)發(fā)生異常的時(shí)候該模塊將調(diào)用一個(gè)redirecttomessage方法,提示一個(gè)友好的錯(cuò)誤界面,告訴請(qǐng)求的用戶有錯(cuò)誤發(fā)生。那么cs系統(tǒng)是如何在發(fā)生錯(cuò)誤的時(shí)候自動(dòng)調(diào)用redirecttomessage方法轉(zhuǎn)向另外一個(gè)頁(yè)面提示友好錯(cuò)誤的呢?先打開(kāi)communityservercomponents項(xiàng)目下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
}
}
文件太長(zhǎng),我們抓出關(guān)鍵的部分來(lái)分析:
public delegate void csexceptionhandler(csexception csex, cseventargs e);
這里先申明一個(gè)委托,相當(dāng)于一個(gè)函數(shù)指針。在通俗一點(diǎn)理解它就是一個(gè)跑腿的,專管傳遞對(duì)象與對(duì)象間的調(diào)用信息。
接下來(lái):
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;
}
這段很重要,通過(guò)讀取communityserver.config文件的<csmodules>,初始化每個(gè)csmodule,注意,初始化后并且調(diào)用了這些csmodule中的init方法。具體看看這些module中的init都做了什么,打開(kāi)communityservercomponents項(xiàng)目下的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);
}
}
}
哈哈,原來(lái)在init方法里把一個(gè)csexceptionhandler委托添加到csexception事件上,這個(gè)委托指向csa_csexception方法,還是通俗點(diǎn)說(shuō):如果csexception這個(gè)事件發(fā)生了,csexceptionhandler這個(gè)跑腿的委托就會(huì)馬上告訴csa_csexception方法要他執(zhí)行,如果事件沒(méi)有被激發(fā)就什么也不做。
名詞: event 關(guān)鍵字使您得以指定當(dāng)代碼中的某些“事件”發(fā)生時(shí)調(diào)用的委托。此委托可以有一個(gè)或多個(gè)關(guān)聯(lián)的方法,當(dāng)代碼指示該事件已發(fā)生時(shí)將調(diào)用關(guān)聯(lián)的方法。
那么這個(gè)csexception又是怎么回事?在哪里定義的?我們回到csapplication.cs文件中,看樣幾行:
public event csexceptionhandler csexception
{
add{events.addhandler(eventunhandledexception, value);}
remove{events.removehandler(eventunhandledexception, value);}
}
這里定義了一個(gè)csexception事件,而事件發(fā)生的時(shí)候只能用csexceptionhandler這個(gè)委托來(lái)做跑腿的。其實(shí)cs中是把委托都存放在了一個(gè)eventhandlerlist中,因此此處你可以看到add與remove, 這是訪問(wèn)器的聲明,用于添加或移除客戶代碼中的事件處理程序,這樣做的好處是公開(kāi)大量的事件但不為每個(gè)事件分配字段,而是使用eventhandlerlist存儲(chǔ)這些事件實(shí)例。為了理解事件的調(diào)用執(zhí)行過(guò)程,我們還必須看幾個(gè)文件:csevents.cs、cseventargs.cs:
cseventargs.cs存儲(chǔ)事件的數(shù)據(jù),這個(gè)很好理解,它繼承自eventargs。當(dāng)事件發(fā)生時(shí)cseventargs用來(lái)傳遞事件的信息,這里傳遞兩個(gè)值:objectstate與applicationtype(可以在enumerations文件夾下找到這兩個(gè)枚舉的內(nèi)容)
csevents.cs這是對(duì)事件調(diào)用的一個(gè)包裝器,看異常處理的包裝:
public static void csexception(csexception csex)
{
csapplication.instance().executecsexcetion(csex);
}
這里先調(diào)用csapplication.instance()方法,實(shí)例化一個(gè)csapplication對(duì)象,如果你是第一次調(diào)用instance()方法,就實(shí)例化所有在<csmodules>中配置的類,并且調(diào)用他們的init方法(在csmodules中配置的這些類,都實(shí)現(xiàn)了icsmodule接口,而這個(gè)接口要求繼承他的類都具備init方法),執(zhí)行init方法的目的就是把委托添加到事件上,使委托指向的方法可以在事件觸發(fā)的時(shí)候被調(diào)用。實(shí)例化后再調(diào)用executecsexcetion方法并且傳遞csexception的實(shí)例,executecsexcetion方法如下:
internal void executecsexcetion(csexception csex)
{
csexceptionhandler handler = events[eventunhandledexception] as csexceptionhandler;
if (handler != null)
{
handler(csex,new cseventargs());
}
}
先通過(guò)對(duì)eventhandlerlist索引訪問(wèn),即events[eventunhandledexception],從列表中找到這個(gè)csexceptionhandler事件,如果不為null就執(zhí)行它。eventunhandledexception又是什么,其實(shí)這只是一個(gè)key,用來(lái)標(biāo)示存儲(chǔ)的事件。
有必要總結(jié)一下,不然你會(huì)被這種調(diào)用來(lái)調(diào)用去的關(guān)系搞得一頭霧水,
以異常處理為例:
1:在錯(cuò)誤發(fā)生后,調(diào)用application_onerror方法;
2:在方法的最后調(diào)用csevents.csexception(csexception);
3:進(jìn)入csevents包裝器,調(diào)用csapplication.instance().executecsexcetion(csex);
4:執(zhí)行csapplication.instance()方法,如果是第一次執(zhí)行就根據(jù)communityserver.config文件中的配置,把所有的csmodules實(shí)例化,并且調(diào)用icsmodule接口類中的init方法,然后緩存這些實(shí)例化的類(如果是第二次訪問(wèn)就從緩存中讀取)。
5:在實(shí)現(xiàn)icsmodule接口的類中,如csexceptionmodule.cs,init方法是給事件添加委托的過(guò)程,這個(gè)過(guò)程中實(shí)現(xiàn)了委托指向的一個(gè)或者多個(gè)方法與事件進(jìn)行關(guān)聯(lián),異常處理的方法csa_csexception(csexception csex, cseventargs e)就是在這里被關(guān)聯(lián)到異常事件上的。
6:經(jīng)過(guò)上面幾步后,cs系統(tǒng)接著調(diào)用executecsexcetion方法,在executecsexcetion方觸發(fā)了csexception事件
7:csexception事件被觸發(fā)后,就執(zhí)行事件中委托所指向的函數(shù),這里是csexceptionmodule.cs文件中的private void csa_csexception(csexception csex, cseventargs e)。
cs如此大量的使用delegates與events帶來(lái)了什么,也許你會(huì)認(rèn)為它這樣是把問(wèn)題復(fù)雜化,而且覺(jué)得這非常沒(méi)有必要,完全可以在異常處理的最后調(diào)用處理方法即可,何必通過(guò)事件來(lái)回周轉(zhuǎn)!最后說(shuō)明一下這樣做的重要性:
1:通過(guò)事件使調(diào)用方法者與方法本身隔離,如在cshttpmodule.cs文件中的application_onerror方法觸發(fā)csevents.csexception事件,而事件要做些什么處理,需要調(diào)用什么方法application_onerror根本不知道。如果你要改變csevents.csexception事件處理方法的結(jié)構(gòu),甚至十處理方法的名稱,application_onerror也不需要改動(dòng),因?yàn)樗静魂P(guān)心具體的實(shí)現(xiàn),它的任務(wù)只是觸發(fā)這個(gè)事件。
2:如果你想一個(gè)方法調(diào)用多個(gè)方法,普通的做法就是在方法中一次調(diào)用或者在方法中嵌套調(diào)用。這樣做并不是一個(gè)好的設(shè)計(jì)模式,而事件可以通過(guò)委托調(diào)用多個(gè)委托指向的方法(在異常處理中只指向了一個(gè)方法,當(dāng)然你可以指向任意n個(gè)方法),而這種調(diào)用也是相互隔離的,被調(diào)用的方法并不致到誰(shuí)調(diào)用它,而調(diào)用者也不關(guān)心它調(diào)用誰(shuí)。
3:模塊化,你?
新聞熱點(diǎn)
疑難解答
圖片精選