熱點推薦:
您现在的位置: 電腦知識網 >> 編程 >> .NET編程 >> 正文

細節決定成敗 ASP.NET中的蝴蝶效應

2013-11-13 11:07:35  來源: .NET編程 

  前言

  ASPNET的優點我說過很多次了也就是各個控件獨立負責自己內部的邏輯這是一個好事情因為它解決了原本ASP處理邏輯耦合度高的問題然而這是需要代價的那就是引入ASPNET頁面生命周期隨著控件的多層嵌套應用的復雜度增加我們再次陷入泥潭!

  問題

  其實這個文章題目我兩個月前就寫下了可是一直沒想寫完它直到今天我在這個泥潭中泡了幾個小時於是決定先從泥潭中跳出來把文章寫完再跳進去繼續解決問題問題是這樣的:

  使用MS AJAX Beta + CTP新建一個項目同時在Bin中放上Beta的AjaxControlToolkitdll

  扔上一個Accordion放置幾個AccordionPane設置一下CssClass

  在Page_Load中使用PageLoadControl加載一個UserControl然後添加到頁面上

  接著發現UserControl內的控件無法正常觸發事件陷入泥潭中……

  首先要說明如果僅僅做第步那個UserControl肯定正常運作那意味著問題出在ScriptManager或Accordion中出現了問題

  正文

  想知道到底是什麼出問題了嗎?先聽我說說這個ASPNET頁面生命周期的問題吧

  由於生命周期按階段劃分任務在不同階段按部就班完成所以我們的每一個操作都是階段相關的有些操作僅能在特定的階段操作有些操作在不同階段執行會導致不同的結果當然MS希望盡量消除這些階段間的差異例如讓一個操作在盡可能多的階段中都能執行並且盡可能減少在不同階段中操作引發的不同結果然而這不可能完全做到例如我們都知道ViewState讀寫限制為僅能在某些階段進行於是依賴於ViewState的控件屬性也就因此受到同樣的限制

  控件屬性讀寫受階段限制這很好接受對吧?因為這僅僅是一層依賴關系順著依賴關系推廣出去情況會變得越來越復雜限制的原因埋藏得越來越底層接著我們發現復雜性這一問題在ASPNET這種結構良好的體系中出現了而消滅這種復雜性的銀彈還沒被發明

  作為控件或組件的開發人員我們當然有義務消除階段差異讓下游的開發人員面對更低的復雜性而且我們也確實盡力去做了控件的每一層封裝都包含著這種努力並向上承諾盡可能低的階段差異然而為了讓控件看起來簡單易用我們不可能將這些差異完整地記錄在文檔之中我們嘗試去隱瞞細節控件被層層封裝時我們都這樣做底層文檔沒告訴我的差異我當然也沒必要寫到這一層的文檔上去;底層文檔提及了的差異我盡力彌補了即使彌補得不太好也不寫到這一層的文檔上去於是文檔就好像神話傳說一樣隨著世代相傳而改變最終沒有人知道這個控件依賴於某些底層的階段差異

  做過控件開發的人都知道有時候我們必須根據實際情況采用不同的方式構建看起來一樣的控件例如最簡單的數據控件都會存在是否PostBack的構建差異如果是非PostBack則需要在DataBind時構建並將數據保存到ViewState如果是PostBack則根據ViewState直接構建如果PostBack後又遇到了DataBind則需要清除原來的構建並重新根據新數據構建再復雜一些的控件還會分步驟構建默認情況下為了消除使用方的階段差異部分構建步驟會盡可能靠前到Init時執行而另外一部分構建步驟則盡可能推遲到PreRender時執行中間部分則盡可能減少自己的變化以便使用方操作然而事情不會那麼簡單使用方的某些操作(通常是訪問某個屬性)如果依賴於某個構建步驟的完成因此一旦這些操作出現原本在PreRender才執行的特定構建步驟就要提前執行當這樣的操作在不同階段進行多幾次構建步驟就已經散落在頁面生命周期的各階段

  構建步驟可能散落於頁面生命周期的各階段對於控件設計師來說是一個嚴峻的問題這意味著他要保證任何一個構建步驟在任何一個階段執行都是無差異的當然這不可能做到於是又要引入別的機制來減少這種差異復雜性就此產生了接下來隨著復雜性的增加控件設計師越來越無法確保較低的階段差異程度這就到控件使用者遭殃了如果控件使用者又再把控件封裝並且依然企圖降低階段差異程度那麼災難也就發生了……

  結果

  我花了幾個小時在泥潭中泡了幾個小時邊泡邊寫這篇文章問題當然已經有結果了

  如果Accordion設置了HeaderCssClass或者ContentCssClass那就會出問題但如果為AccordionPane都加上以上兩個屬性又不會有問題了這樣的情況當然通過用Reflector查看這兩個類的代碼來解決結果發現Accordion會檢測每一個AccordionPane是否有設置這兩個屬性如果沒有就把AccordionPane的設置為和自己的一樣在AccordionPane被設置時會調用thisEnsureChildControls()這是一個會導致構建步驟提前執行的方法於是控件構建的順序就改變了不僅僅Accordion內部的順序改變了整個Page的都改變了由於控件的ID是按順序自動分配的包括我那個UserControl構建順序的改變意味著ID的改變也就相當於整個控件樹都改變了事件當然不能正常觸發

  最後的解決方案當然是為我那個UserControl指定ID我花了那麼多個小時才發現自己做了件蠢事一早打開Trace來看控件樹就應該能發覺UniqueID的變化

  總結

  雖然這個問題看起來不是一個太好的例子因為一打開Trace就應該能找到問題的來源但實際上它卻正好揭示了ASPNET框架內部的蝴蝶效應(Butterfly Effect)——隨著復雜度的增加任何一個細微的改變都會導致全局上的巨大變化在設計ASPNET的時候MS可能也在想著解耦在簡單的情況下這東西確實也解耦然而在復雜的情況下卻正好背道而馳這真的是很諷刺


From:http://tw.wingwit.com/Article/program/net/201311/15217.html
    推薦文章
    Copyright © 2005-2013 電腦知識網 Computer Knowledge   All rights reserved.