2009年3月2日

ASP.NET 2.0+ Complete Event Sequence

對於初學 ASP.NET 的新手來說,搞清楚 ASP.NET 內建的數十種 event 的執行順序是非常重要的(對於專業開發 control 的人應該是超重要的),特別是在處理 Data Binding 的時候更是如此,首先必須大略瞭解 ASP.NET Engne (或者應該說 .aspx 的 HttpHandler) 依序對一些重要系統資源做了甚麼處理(e.g., Load SessionState、Load PageViewState、Run Page Constructor、Unload Control..),才會知道若要對某些資源作處理時,可以把程式寫在哪個 event 的 event handler 中。

關於 event 執行的順序,在 MSDN 上通常叫做「XXX Lifecycle」,例如 Application LifecyclePage Lifecycle 等,雖然各別來看 Application Level &Page Level 的 event sequence 都很清楚,但是 Application events 和 Page events 之間的先後順序呢?

更嚴重的問題是,若採用貼近 Microsoft 所建議的開發模式,Page 上免不了會套用 MasterPage、UserControl、和 Custom Control,這些 Control(這個詞好像不太好=.=)都有自己的 code-behind,因此也都有自己的 Init、Load、Unload 等等 event,
那麼這些 events 跟 Application 和 Page events 之間的先後順序又是怎麼樣呢?
 
而且不要忘記了,MasterPage 上可以擺 UserContrl,而 UserControl 上還可以使用 CustomControl 呢
(喜歡自己用 JSON 手工打造 UI 的網站可能不會用到這麼多東西)


幸好透過偉大的 Google,我們可以找到ranking 很高的示意圖,但是!製圖時間已經是 2004 年,也沒有把 Application Event 納入。

另外一個更新的 reference 是 Jeff 這個好人的 blog post,但看了下面的留言就會發現,這是根據某個 pre-beta 版本而做出來的結果,不符合 production 版的 ASP.NET 2.0 的行為。(經過實測也發現,有些 Jeff 列出的 event 現在已經被拿掉了)
 
(Update 3/1: 這應該就是親愛的小馬王同學的 reference 來源吧?)


那麼到底要如何能夠隨時隨地確認 ASP.NET 的 complete event sequence 呢?!
當然是老老實實的一個一個去實作每個 event 的 event handler,然後以 trace/log 的方式來紀錄事件觸發的順序呀!

為了驗證 ASP.NET 2.0+ 的完整 event sequence,我做了一個簡單的 application,一一實作每個 event 的 event handler,在其中用 log4net 將 event 資訊寫到 .csv 檔中。

為了區別事件是在哪裡發生的,我用了「MasterPage」、「ContentPage」、「UserControlPage」和「CustomControl」等 prefix,並且輸出 UserControl 和 CustomControl 的 ClientID,以利識別 control 所在的頁面。

稍微觀察一下就會發現一些有趣的地方,例如 DataBinding 相關 event 被觸發的次數、相同事件 (e.g., Init) 在不同 Page 會有不同的順序,Session 載入的時機 … 等等。

基本上目前實作的 event handler 都是參考 MSDN 的說明,Jeff 所列出的 event 有些很有趣,例如與 ViewState 相關的 event,但是我挑了幾個來實作都無效,砍尼有空可以再幫我加強這個部份 :p

經過一些整理,完整的 event sequence 長得像下面這個樣子:
(好的 visualization 真的很重要呀 … 這裡論上應該可以弄成一顆樹,會比較好看)
Application_Start
Application_BeginRequest
Application_AuthenticateRequest
Application_PostAuthenticateRequest
Application_AuthorizeRequest
Application_PostAuthorizeRequest
Application_ResolveRequestCache
Application_PostResolveRequestCache
ContentPage_Constructor
Application_PostMapRequestHandler
Session_Start
Application_AcquireRequestState
Application_PostAcquireRequestState
Application_PreRequestHandlerExecute
ContentPage_PreInit
MasterPage_Constructor
UserControlPage_Constructor_
UserControlPage_Constructor_
UserControlPage_Constructor_
UserControlPage_Constructor_
UserControlPage_Constructor_
UserControlPage_Constructor_
CustomControl_OnInit_ctl00_ContentPlaceHolder1_MyUserControl1_MyCustomControl1
ContentPage_GridView1_Init_ctl00_ContentPlaceHolder1_MyUserControl1_GridView1
UserControlPage_Init_ctl00_ContentPlaceHolder1_MyUserControl1
CustomControl_OnInit_ctl00_ContentPlaceHolder1_MyCustomControl1
ContentPage_GridView1_Init_ctl00_ContentPlaceHolder1_GridView1
CustomControl_OnInit_ctl00_MyUserControl1_MyCustomControl1
ContentPage_GridView1_Init_ctl00_MyUserControl1_GridView1
UserControlPage_Init_ctl00_MyUserControl1
CustomControl_OnInit_ctl00_MyCustomControl1
ContentPage_GridView1_Init_ctl00_GridView1
MasterPage_Init
ContentPage_Init
ContentPage_InitComplete
ContentPage_PreLoad
ContentPage_Load
MasterPage_Load
UserControlPage_Load_ctl00_ContentPlaceHolder1_MyUserControl1
CustomControl_OnLoad_ctl00_ContentPlaceHolder1_MyUserControl1_MyCustomControl1
ContentPage_GridView1_Load_ctl00_ContentPlaceHolder1_MyUserControl1_GridView1
CustomControl_OnLoad_ctl00_ContentPlaceHolder1_MyCustomControl1
ContentPage_GridView1_Load_ctl00_ContentPlaceHolder1_GridView1
UserControlPage_Load_ctl00_MyUserControl1
CustomControl_OnLoad_ctl00_MyUserControl1_MyCustomControl1
ContentPage_GridView1_Load_ctl00_MyUserControl1_GridView1
CustomControl_OnLoad_ctl00_MyCustomControl1
ContentPage_GridView1_Load_ctl00_GridView1
ContentPage_LoadComplete
ContentPage_PreRender
MasterPage_PreRender
UserControlPage_PreRender_ctl00_ContentPlaceHolder1_MyUserControl1
ContentPage_GridView1_DataBinding_ctl00_ContentPlaceHolder1_MyUserControl1_GridView1
ContentPage_GridView1_RowCreated_ctl00_ContentPlaceHolder1_MyUserControl1_GridView1
ContentPage_GridView1_RowDataBound_ctl00_ContentPlaceHolder1_MyUserControl1_GridView1
ContentPage_GridView1_RowCreated_ctl00_ContentPlaceHolder1_MyUserControl1_GridView1
ContentPage_GridView1_RowDataBound_ctl00_ContentPlaceHolder1_MyUserControl1_GridView1
ContentPage_GridView1_RowCreated_ctl00_ContentPlaceHolder1_MyUserControl1_GridView1
ContentPage_GridView1_RowDataBound_ctl00_ContentPlaceHolder1_MyUserControl1_GridView1
ContentPage_GridView1_RowCreated_ctl00_ContentPlaceHolder1_MyUserControl1_GridView1
ContentPage_GridView1_RowDataBound_ctl00_ContentPlaceHolder1_MyUserControl1_GridView1
ContentPage_GridView1_RowCreated_ctl00_ContentPlaceHolder1_MyUserControl1_GridView1
ContentPage_GridView1_RowDataBound_ctl00_ContentPlaceHolder1_MyUserControl1_GridView1
ContentPage_GridView1_DataBound_ctl00_ContentPlaceHolder1_MyUserControl1_GridView1
ContentPage_GridView1_PreRender_ctl00_ContentPlaceHolder1_MyUserControl1_GridView1
ContentPage_GridView1_DataBinding_ctl00_ContentPlaceHolder1_GridView1
ContentPage_GridView1_RowCreated_ctl00_ContentPlaceHolder1_GridView1
ContentPage_GridView1_RowDataBound_ctl00_ContentPlaceHolder1_GridView1
ContentPage_GridView1_RowCreated_ctl00_ContentPlaceHolder1_GridView1
ContentPage_GridView1_RowDataBound_ctl00_ContentPlaceHolder1_GridView1
ContentPage_GridView1_RowCreated_ctl00_ContentPlaceHolder1_GridView1
ContentPage_GridView1_RowDataBound_ctl00_ContentPlaceHolder1_GridView1
ContentPage_GridView1_RowCreated_ctl00_ContentPlaceHolder1_GridView1
ContentPage_GridView1_RowDataBound_ctl00_ContentPlaceHolder1_GridView1
ContentPage_GridView1_RowCreated_ctl00_ContentPlaceHolder1_GridView1
ContentPage_GridView1_RowDataBound_ctl00_ContentPlaceHolder1_GridView1
ContentPage_GridView1_DataBound_ctl00_ContentPlaceHolder1_GridView1
ContentPage_GridView1_PreRender_ctl00_ContentPlaceHolder1_GridView1
UserControlPage_PreRender_ctl00_MyUserControl1
ContentPage_GridView1_DataBinding_ctl00_MyUserControl1_GridView1
ContentPage_GridView1_RowCreated_ctl00_MyUserControl1_GridView1
ContentPage_GridView1_RowDataBound_ctl00_MyUserControl1_GridView1
ContentPage_GridView1_RowCreated_ctl00_MyUserControl1_GridView1
ContentPage_GridView1_RowDataBound_ctl00_MyUserControl1_GridView1
ContentPage_GridView1_RowCreated_ctl00_MyUserControl1_GridView1
ContentPage_GridView1_RowDataBound_ctl00_MyUserControl1_GridView1
ContentPage_GridView1_RowCreated_ctl00_MyUserControl1_GridView1
ContentPage_GridView1_RowDataBound_ctl00_MyUserControl1_GridView1
ContentPage_GridView1_RowCreated_ctl00_MyUserControl1_GridView1
ContentPage_GridView1_RowDataBound_ctl00_MyUserControl1_GridView1
ContentPage_GridView1_DataBound_ctl00_MyUserControl1_GridView1
ContentPage_GridView1_PreRender_ctl00_MyUserControl1_GridView1
ContentPage_GridView1_DataBinding_ctl00_GridView1
ContentPage_GridView1_RowCreated_ctl00_GridView1
ContentPage_GridView1_RowDataBound_ctl00_GridView1
ContentPage_GridView1_RowCreated_ctl00_GridView1
ContentPage_GridView1_RowDataBound_ctl00_GridView1
ContentPage_GridView1_RowCreated_ctl00_GridView1
ContentPage_GridView1_RowDataBound_ctl00_GridView1
ContentPage_GridView1_RowCreated_ctl00_GridView1
ContentPage_GridView1_RowDataBound_ctl00_GridView1
ContentPage_GridView1_RowCreated_ctl00_GridView1
ContentPage_GridView1_RowDataBound_ctl00_GridView1
ContentPage_GridView1_DataBound_ctl00_GridView1
ContentPage_GridView1_PreRender_ctl00_GridView1
ContentPage_SaveStateComplete
CustomControl_Render_ctl00_ContentPlaceHolder1_MyUserControl1_MyCustomControl1
CustomControl_Render_ctl00_ContentPlaceHolder1_MyCustomControl1
CustomControl_Render_ctl00_MyUserControl1_MyCustomControl1
CustomControl_Render_ctl00_MyCustomControl1
CustomControl_Unload_ctl00_ContentPlaceHolder1_MyUserControl1_MyCustomControl1
CustomControl_Dispose_ctl00_ContentPlaceHolder1_MyUserControl1_MyCustomControl1
ContentPage_GridView1_Unload_ctl00_ContentPlaceHolder1_MyUserControl1_GridView1
UserControlPage_Unload_ctl00_ContentPlaceHolder1_MyUserControl1
CustomControl_Unload_ctl00_ContentPlaceHolder1_MyCustomControl1
CustomControl_Dispose_ctl00_ContentPlaceHolder1_MyCustomControl1
ContentPage_GridView1_Unload_ctl00_ContentPlaceHolder1_GridView1
CustomControl_Unload_ctl00_MyUserControl1_MyCustomControl1
CustomControl_Dispose_ctl00_MyUserControl1_MyCustomControl1
ContentPage_GridView1_Unload_ctl00_MyUserControl1_GridView1
UserControlPage_Unload_ctl00_MyUserControl1
CustomControl_Unload_ctl00_MyCustomControl1
CustomControl_Dispose_ctl00_MyCustomControl1
ContentPage_GridView1_Unload_ctl00_GridView1
MasterPage_Unload
ContentPage_Unload
Application_PostRequestHandlerExecute
Application_ReleaseRequestState
Application_PostReleaseRequestState
Application_UpdateRequestCache
Application_PostUpdateRequestCache
Application_EndRequest
Application_PreSendRequestHeaders
Application_PreSendRequestContent
Session_End
Application_Disposed
Application_End

我的測試網站程式可以在這裡下載,跑出來的 csv 檔可以在這裡下載。
PS. log4net 幫我寫的第一筆紀錄的時間格式很詭異,開頭多了一個「þÿ」,如果有高手知道這是甚麼鬼東西,麻煩指點一下 :D

1 則留言:

坎尼 提到...

原來你和 PY 的關係那麼親密啊 XDDD

生命週期這東西我都是遇到時才寫程式去測
感謝 Tim 做了測試,以後可以少費點工啦 哈

Google Spreadsheet 裡用規則運算式

最近因為工作關係,遇到要用 Google Form 及 Google Sheet 所以研究了 Google Sheet 裡的一些 function 怎麼用 首先,分享一下如何在 Google Sheet 裡用規則運算 :D