2008年6月26日 星期四

ASP.net 1.1 -- (*@__@*) --> ASP.net 2.0

  .NET 1.1 已經是微軟不可能再回頭支援的過時玩意了,.NET 2.0 的核心才是他未來技術發展的基石 ( 目前推出的 .NET 3.5 仍是以 2.0 為底層 )。對於微軟這種全面性推翻舊東西硬是逼著你升級的做法,我是很感冒,由其是對於過去 1.1 時代就選擇支持它的群眾,如今卻被逼著必須同時安裝多套不同的 IDE 來回在不同版本的程式間作維護。

  如今許多方便好用的功能都已經內建在  .NET 2.0 之上,回頭看看當初 1.1 寫的東西總是很想掉下眼淚,無奈自己已經誤上賊船,為了降低將來維護成本並且增加未來發展的視野性,最近幾天就鐵下心來,籌畫著將私底下仍在經營的網站,升級到 ASP.NET 2.0。

  為了讓整個升級過程無痛,我不想讓使用者發覺停機過久,盡量讓網站無法運作的時間縮短在一個「瞬間」,所以我決定利用 ASP.NET 2.0 / CLR 2.0 仍可以執行 1.1 版本的向下相容特性,決定先透過變更 IIS 內的版本設定讓 ASP.NET 1.1 開發的網站先執行在 2.0 的環境底下,等到平穩後,再找個機會以迅雷不及掩耳的速度,將重新編譯過的新版網站上傳到 FTP 蓋過舊的,這個過程理應只有幾個線上的使用者的 Session 失效,重新登入或是重新整理網頁就一切正常。

  其實整個過程說來蠻單純的,風險應該不大,但讓我拖了一年半載遲遲沒有動手的原因,主要就是用 ASP.NET 2.0 去執行 ASP.NET 1.1 的向下相容性並沒有很好,實際跑起來還遇到蠻多問題的,一直讓我怯步。

  首先,為了讓我的網站能同時支援以不同語言來顯示畫面 ( 目前同時支援 繁中 / 簡中 / 英文 的操作畫面 ),所以我網頁上的顯示文字是根據使用者下拉語言的選單動態產生的。我實做的方式很簡單,就是以一個 XML 檔儲存所有控件顯示的文字訊息。例如我有一個網頁 Index.aspx 上面還有一個顯示訊息的 Label ,其 ID = Label_Welcome,那我在繁中的 XML 語言檔 ( zh-tw.xml ) 就是如下:

<root>
    <Index>
        <Label_Welcome>歡迎光臨</Label_Welcome>
    </Index>
</root>

  如果是英文的語言檔 ( en-us.xml ) 就如下:

<root>
    <Index>
        <Label_Welcome>WELCOME!!</Label_Welcome>
    </Index>
</root>

  要讀取哪個檔案依下拉選單來決定,然後在每個 page 的 _PreRender() event 中依 [ 頁面類別名稱 + 控件 ID ] 組成的 XPath 去做顯示文字的讀取。但事實上,ASP.NET 在實際執行的時候,使用的不是你原本開發時撰寫的 class,而是它動態產生出來、繼承自你頁面 class 的新 class,而你當初編譯完成的 dll 其實也只是被它拿來當作 Code-Gen 引用參考的對象而已。

  ASP.NET 動態產生出來的頁面執行 class 通常命名為以下的規則:[ 你撰寫的頁面 class
名稱 + "_aspx"
],為此,如上述的網頁實際上會產生 Index_aspx 的新類別,所以我在使用
this.GetType().Name 組合 XPath 時,需要做點後製加工。當時的做法是把後面尾巴產生出的 "_aspx" 硬切掉 ( this.GetType().Name.Replace("_aspx", string.empty) )。

  但是不知何故,切換到 ASP.NET 2.0 的 runtime 執行,這個做法就失效了,整個頁面一整個空白,完全顯示不出任何文字。

  由於 VS 2003 無法 Attach 上 .NET 2.0 的 runtime process 做 debug,我也懶得修改程式吐 OutputDebugString,所有就沉潛了好一陣子置之不理。直到前幾個月突然心血來潮,一研究才發現到了 ASP.NET 2.0,動態產生的 Code-Gen 的命名規則改了,不管你原本的名字的大小寫,它會一律轉為全部小寫!所以我原本的 Index 會變成 index_aspx ( 這是哪個微軟的白痴工程師做出來的向下相容性啊?連這種無關緊要 naming rule 的小地方都不相容... )。去掉尾巴後,index 和 Index 由於大小寫的差異造成 XPath 讀取不到正確的顯示文字。

  知道問題所在後,改為直接使用 GetType().BaseType.Name 來解決此問題,寫法也比直接處理字串加工來得簡潔合理。

  測試一段時間看來沒太大問題,放上網站後,填申請表請虛擬主機商的工程人員幫我切換到 2.0 runtime,然後就靜悄悄地等幾天,也無法預知他何時會做轉換。所以這就是為何我非得經歷 2.0 跑 1.1 後,再轉換到 pure 2.0 的過程,因為無法預料對方何時幫我轉換設定

  不過看來昨天主機商下午幫我調整過設定,我信箱中收到一堆系統錯誤信件,發現所有線上交易都已經停擺了數小時。

  緊急調查下發現系統異常信件中提到:

Exception Content :
System.Web.HttpUnhandledException:
已發生類型 'System.Web.HttpUnhandledException' 的例外狀況。
---> System.ArgumentException:

無效的回傳或回呼引數。
已在組態中使用
或在網頁中使用 <%@ Page EnableEventValidation="true" %> 啟用事件驗證。

基於安全性理由,這項功能驗證回傳或回呼引數是來自原本呈現它們的伺服器控制項。如果資料為有效並且是必需的,請使用
ClientScriptManager.RegisterForEventValidation 方法註冊回傳或回呼資料,以進行驗證。"

  原來之前因為多層選單使用 ASP.NET 的 AutoPostBack 機制,嚴重讓使用者抱怨,故我修改為輸出許多 JScript 在瀏覽器端處理多重下拉選單的更新問題,但到了 2.0 ,這些 JScript 會動態修改原本 ServerControl 輸出時的內容造成和原本的 ViewState 產生差異,導致安全性驗證失敗。好在立刻在該頁面加註 [ EnableEventValidation="false" ] 關閉驗證就解決此問題。

  但是事情還沒結束,早上又再次收到一堆系統錯誤訊息,發現日期字串無法識別?

  "System.FormatException:字串未被辨認為有效的 DateTime

  仔細檢查程式碼,才發現之前我在組合日期字串時錯用了斜線符號:[ 2008//06//27 10:00 ],我大概老眼昏花,誤以為這是跳脫字元所使用的反斜線 ( \ ) ,所以都重複鍵入兩次。但這樣的日期解析,在 .NET 1.1 是可以被正確識別的

  好吧,又一個緊急上版修正這個問題。

  就這樣結束了嗎?不,這篇文章尚未完成的當下,我信箱內再度出現使用者抱怨的信件,說所有的電子訂單的金額都變成 0 ,要我緊急修復!God!再跳下去查,原來過去我為了增加效能,把部分 ServerControl 的 ViewState 給 Disable ,尤其是 TextBox 。

  因為我發現即使關閉 TextBox 的 ViewState ,因為 Submit Button 按下去時,瀏覽器照樣會將使用者輸入的資料回傳, ASP.NET 照樣會在初始化的時候將將該 value 填入控制項內,並不會取不到資料,功效相同卻不需要額外存放 Text Value 以外的其他 Properties。

  同樣,到了 2.0 ,這樣的做法式不行的,一切以 ViewState 驗證過的資料為主,於是乎沒有 ViewState 的 TextBox Control 瞬間被釘死。

  方才又修正好這個問題,不僅要抱怨一下,微軟的一個升級,在小細節上實在有太多不相容與不一致性了。這次轉換後,短時間不敢再輕易嘗試原本計畫中的下半場步驟 ---> 整個網站重新編譯成原生的 ASP.NET 2.0 版本現在是在 2.0 Engine 下以相容模式跑 1.1 的編譯產物 ) 。

2008年6月13日 星期五

BizTalk 之我思我見



  完成連續四天的課程後,昨天從恆逸拿到我這輩子第一張電腦技能的修業證書,沒想到既不是 .NET ,也不是 Java,竟然是做夢也沒想到的 BizTalk 2006

  雖然上面印著 Certificate 的 Title ,但這只是個無足輕重的修業證明,並不是證照。然而岳父卻非常看重這個證照橫行的時代,每每面對我這個除了駕照以外一無所有的軟體工程師,總是碎碎念,認為我技不如人、遲早被社會淘汰

  Anyway,這張假證照或許日後可以幫我唬弄過去,反正他甚麼也搞不懂。

  對於有程式開發背景的人來說, BizTalk 大體上並不難理解與入手,一本我目測約三百多頁的原文教材,包含上機實做練習,四個全天就講完了。但是我極度懷疑沒有程式開發背景的人員能夠理解 BizTalk 這套軟體的操作,所以基本上,我看待這個產品並不是取代程式開發人員的工具,但「或許」是節省開發人員時間與負擔的工具。

  強調「或許」,是對於開發老手,他早以手邊累積不少現成的套件或是函式庫,熟悉 OpenSource 的開發資源與現成的 Class,旁徵博引的開發效率未必會比滑鼠設定 BizTalk 內的各個 Pipeline / Adapter / Schema 來得慢,甚至可能還快上許多。但是對於新進的開發人員,BizTalk 提供許多現成的基礎環境設施與視覺化設定,應該可以幫助菜鳥進入實戰階段,所以充其量,BizTalk 是個拉近新手和老手之間差距的工具。

  也許有人會說,產品的設定與視覺化開發比起程式碼來得好維護,這應該是 BizTalk 該勝出的地方,但事實卻不然。

  我承認菜鳥工程師的程式碼解讀時會冒出無名火,但現在比較的狀況是,[ 經驗老道的工程師  vs ( 菜鳥工程師 + BizTalk ) ] ,如果常常去看 OpenSource 的開發人員會知道,有經驗的開發人員寫出的程式碼其實不難端倪,而且可修改的彈性高出甚多,逐步除錯也容易。但是,BizTalk
的除錯非常困難,且修改不易。整個上課過程中,我就飽受「一步錯,整個重來」的痛苦,一個 Schema 建錯,可能牽涉到的 Flow、Mapper、Pipeline、Ports...全部都得跟著調整或是重建,而且如果漏掉任何一個環節沒修正,非常不容易發覺,只知道訊息有進無回。



  說到日後維護容易嘛...,因為圖型介面使得一張圖勝過千言萬語,交接與理解較為容易,這些論點因為我缺乏真正的實務經驗,不作評論 ( 依我所聽到的前人經驗,他人接手前人的專案,其實並沒有比較容易交接或是理解 ) ,但我要強調,即使真的有這個好處也是應該的,公司並不會因為維護容易而降低維護成本增加獲利,因為你帶入產品進去做,每年的維護費用收入本來就會有部分被產品的原廠 ( ex. 微軟** ) 拿走,公司真正能從每年維護營利的金額本來就比完全自己做來得低許多。但如果帶了 BizTalk 進去做,維護成本沒降低多少,那開發廠商豈不是虧到沒衛生紙擦屁股?

  目前我參觀過的幾個 BPM Solution - TIBCO、IBM WebLogic 和 BizTalk,微軟的 BizTalk
是最為技術人員導向的產品,也是商務功能最弱的。

  這幾天上課,我嘗試分析 BizTalk 的成本效應。以它標準版 ( Standard Edition ) 的售價每顆 CPU 索價台幣 30 萬,中小型標案帶入不划算,價錢也無競爭力,但是大型案子要到多大的規模帶入,才能發揮效益產生競爭力呢?

  我的評估方式如下,如果某 SI 廠商的人日報價單價為 NT$10,000 / 人日,引進一顆 CPU 的 BizTalk 標準版授權,就必須至少縮短專案 30 個人日的開發成本才剛好打平。

  如果,客戶使用一台兩顆 CPU 的機器當伺服器,BizTalk Standard 就要額外開銷 60 萬,這還是在不需要買測試機授權的狀況下。然後,假設引進 BizTalk 可以節省 1 / 3 的人日成本,使專案上線時程縮短,如果要剛好達到平衡,BizTalk 的 60 萬開銷就至少必須與這 1 / 3 的人日的成本節省等價。

  剛提到一個人日一萬,60 萬就等於 60 個人日,也就是當總專案預估時程為 180 個人日的規模,引進 BizTalk Standard 版會剛好達成「開發階段」的成本平衡。這只是單論開發階段,也就是前期需求訪談、系統分析 (SA )、系統設計 ( SD ),與後期上線前測試階段系統整合測試 ( SIT )、使用者接受度測試 (UAT )、驗收流程 ( 文件 ) 都無法節省的狀況,專案規模至少要 235 個人日,也就是 235 萬以上的案子帶入 BizTalk 才有獲利的可能。

  以上的試算的情境 Scenario 純屬初估,而且是以單一案子導入為前提 ( 不考慮同一個客戶日後有第二案或是第三案在同一台伺服器上利用 BizTalk 額外建置的狀況,所以說做生意要看遠、做遠就是這個意思 ^_^ )。以下提供簡易的公式僅供導入的評估參考:

  適合導入 BizTalk 的專案規模 = [BizTalk 版本單價] * [CPU 數量] / [導入後預計節省的人日比例 ( 上例中為 1 / 3 ) ] * [130% ( 依比例計算前期與後期-非開發時期的人日成本 )]

  要提醒幾點,導入後能節省 1 / 3 的開發人力是非常非常樂觀的估計,聽有實務經驗的人說能節省 1 / 5 ~ 1 / 10 就偷笑了。其次,懂 BizTalk 的設計師人員的薪資一定比只會  Coding 的菜鳥工程師來的高出不少,就算不影響廠商給客戶的專案報價,也一定會墊高成本侵蝕利潤,這些都是必須再斟酌的。

** 微軟的維護費用不是採取一般廠商所使用的按產品售價比例收取維護費用的方式,而是採取買問題點數的做法。

*** 老師張 x 源其實教的還算有水準,雖然有些小錯誤,但還算用心。我現在都不太敢打別人的全名,因為發現大家都喜歡上 Google 查詢自己的名字。之前在中央聽了一場演講,我把對方講者的全名寫在部落格上,沒隔幾天,該篇文章就出現該演講者的回應跟我打招呼...