March 2007 - Posts

KB-T-SQL找最大值方法的效能比較

今天跟同事討論用T-SQL查資料表中最大值的方法,一群人總共想出三種: SELECT TOP 1 + ORDER BY, SELECT MAX, 再來是用CURSOR的FETCH LAST。三種做法,哪一個最有效率呢? 初步想起來,用CURSOR是最笨重的,肯定最慢。剩下的兩種,MAX()是Aggregate Function,依據我過去寫SQLCLR自訂Aggregate Function的經驗,每一列的資料都要送入Funtion中比較,應該會輸給內建的ORDER BY吧?

找來一個有150萬筆資料的Table,做了以下的實驗:

--方法1 用ORDER BY+TOP
select top 1 dst from netlog order by dst desc

--方法2 用MAX

select max(dst) from netlog

--方法3 用CURSOR

declare @dst varchar(15)

declare cur scroll cursor for select dst from netlog order by dst

open cur

fetch last from cur into @dst

select @dst

close cur

deallocate cur

實驗結果證明我之前的推論是錯的,MAX以750ms奪冠,TOP 1 + ORDER BY以921ms居次,CURSOR則以7203ms被甩到連車尾燈都看不到。仔細分析,ORDER BY必須一筆筆理出所有ROW的順序,對MIN或MAX來說純粹是做虛工,浪費資源卻沒有任何貢獻,是該敗陣。(這裡有一篇相關文章)

結論是,如果你只是要查最大或最小值時,MAX()與MIN()會比TOP 1 + ORDER BY更有效率!

另外,SQL 2005的SQL Server Management Studio(SSMS)的Cient Statistics功能比起SQL 2000 Query Analyzer豪華許多,會自動幫你進行多次執行結果的比較,還會用紅、綠、黑箭頭代表上漲、下跌跟平盤(我好像開始有職業病傾向了)。找不到啟動選單的人可以看以下的圖例:

KB-Thread.Sleep, 別賴床!

上回話說我們觀察到在不同的機器上,Thread.Sleep(1)的結果不一定就是1ms,而可能是以15ms為單位。

經過鍥而不舍的搜查,後來有了驚奇的發現! 原來這個事實是可以改變的,多媒體程式庫中有個timeBeginPeriod的API,可以設定Thread.Sleep的時間解析度,例如以下的寫法:

private static void TestSleep()

{

    timeBeginPeriod(1);

    Thread.Sleep(1);

    timeEndPeriod(1);

}

[DllImport("winmm.dll")]

internal static extern uint timeBeginPeriod(uint period);

[DllImport("winmm.dll")]

internal static extern uint timeEndPeriod(uint period);

於是我在上次的TestThreadSleep程式中加上timeBeginPeriod, timeEndPeriod的邏輯,測試結果馬上大不相同,Thread.Sleep不再懶床,原則上會逼近所要求的等待時間。以DL360為例,

使用前:

Thread.Sleep(25) Test:
23.968 ms, 31.266 ms, 30.447 ms, 31.165 ms, 31.041 ms, Avg = 29.577 ms
Thread.Sleep(15) Test:
15.682 ms, 15.291 ms, 15.557 ms, 15.521 ms, 15.550 ms, Avg = 15.520 ms
Thread.Sleep(10) Test:
15.486 ms, 15.463 ms, 15.682 ms, 15.418 ms, 15.517 ms, Avg = 15.513 ms
Thread.Sleep(5) Test:
15.472 ms, 15.552 ms, 15.535 ms, 15.569 ms, 15.709 ms, Avg = 15.568 ms
Thread.Sleep(1) Test:
15.290 ms, 15.539 ms, 15.559 ms, 15.551 ms, 15.554 ms, Avg = 15.499 ms

使用後:

Thread.Sleep(25) Test:
5.680 ms, 25.015 ms, 25.268 ms, 25.319 ms, 25.117 ms, Avg = 21.280 ms
Thread.Sleep(15) Test:
15.637 ms, 15.420 ms, 15.548 ms, 15.556 ms, 15.560 ms, Avg = 15.544 ms
Thread.Sleep(10) Test:
10.755 ms, 10.576 ms, 10.626 ms, 10.674 ms, 10.536 ms, Avg = 10.633 ms
Thread.Sleep(5) Test:
5.591 ms, 5.684 ms, 5.789 ms, 5.786 ms, 5.798 ms, Avg = 5.730 ms
Thread.Sleep(1) Test:
1.761 ms, 1.783 ms, 1.866 ms, 1.902 ms, 1.735 ms, Avg = 1.810 ms

不過,使用timeBeginPeriod時要注意,原則上調動它會造成系統不必要的負擔,記得不用了馬上timeEndPeriod恢復原狀。

Posted 29 March 2007 02:35 PM by Jeffrey | no comments
Filed under: ,
永不休止的垃圾郵件大戰

我一直很欣賞GMail的垃圾信防護功能,每次看到垃圾信匣中滿滿的垃圾郵件,而收件匣中總是能保持乾淨清爽,都不免讚嘆一番,甚至還一度懷疑是GMail主動塞入垃圾信搞績效的(就像警察要養案做業績一樣),哈!

這幾個月來,開始發現遇爾會有幾封漏網之魚跑到收件匣中,還發現一些好玩的事。

我們都已經很習慣在網路留言或註冊時,要玩一種看圖識字的遊戲,它有個正式學名叫CAPTCHA,目的在防止垃圾留言機器人騷擾。其中有一項技巧就是在圖中加上雜點,目的在增加被OCR的難度,減少被破解的風險。最近我常在漏網的垃圾信中看到這種圖檔: (URL上的灰色是我塗的,完全不想替它做任何廣告。但如果有人真的很需要信中提到的便宜威而剛,請留言,我再給你網址。)

簡直是做賊的喊捉賊~~

近年來防垃圾信機制日益發達,可以由信的內文、收信者等一些特徵判斷是否為垃圾信,於是就有些垃圾信開始把廣告文字藏在圖檔中,接著防垃圾信程式也開始加入OCR功能,能把圖檔中的文字取出來比對,最後連垃圾信中的圖檔都要加上雜點防止被OCR,實在好笑。

防垃圾留言的CAPTCHA圖案上加了雜點防止垃圾留言機器用OCR破解,垃圾信中的廣告文字圖檔中也加上雜點防止反垃圾信程式OCR破解,看來這場大戰還有得打囉!

KB-ASP.NET 2.0 Cookie Bug

同事小熊子反應: Community Server暱名留言時姓名欄位提供的"記住我"功能,在處理中文時會有亂碼的問題,由他使用IE Cookie View追蹤的結果,感覺上ASP.NET 2.0是用UTF-8編碼保存中文,但下次使用時卻姓名變成亂碼也是事實。

印象中在另一位MVP的Blog有看過類似的問題,回頭查了一下,症狀幾乎完全相同,看來是ASP.NET 2.0的Bug(對某些人來說挺要命的)。要閃開這個Bug的一個有效作法是對文字做Encoding,於是我改了CommunityServerBlogs20.Blogs.Controls.CommentForm.cs,在寫入/讀取Cookie時加上UrlEncode及UrlDecode,問題解決,Case Closed。

KB-測試Thread.Sleep的精確度

最近在開發一些很講求速度的程式,對於"程式夠不夠快"這件事有了全新的體認!

以往寫Web時,User多半很能忍受看網頁本來就需要等待這件事實,所以只要不太離諎,3,5秒的Delay多半還可被接受。而最近在處理的案子是那種速度絕對至上的典型,慢了1ms程式就變成廢柴,因此每個環節都要求快,變成不能用傳統正規的做法解決,到處都在走偏鋒,才能達成目標。

CodeProject上有篇很棒的研究--Timer Surprises,看過之後才發現Timer.Interval設成15ms以下根本就是自欺欺人,而文中所附的程式是個很好的實證,可以測試你的電腦如何實現Timter.Interval 1ms, Thread.Sleep 1ms,結果會讓你很吃驚。

在我的兩台號稱旗艦的主機上執行的結果,Thread.Sleep可以達到極接近的精確度,滿心歡喜地想應用在試測環境上,沒想到... 天哪,結果竟不相同。於是我簡化了程式碼,寫了一個Console Application-TestThreadSleep,蒐集手邊幾台機器的執行結果,發現一個很有趣的事實! 如同Timer Surprises文中所說的,作者沒找出決定Thread.Sleep實際停止時間的因素,我也暫無定論(但有些推測)。

以下是我的測試結果:

XPC SD27P2 Core 2 Duo E6400 2.16G Win 2003

Thread.Sleep(25) Test:
25.374 ms, 25.261 ms, 25.337 ms, 25.362 ms, 25.241 ms, Avg = 25.315 ms
Thread.Sleep(15) Test:
15.484 ms, 15.581 ms, 15.580 ms, 15.563 ms, 15.573 ms, Avg = 15.556 ms
Thread.Sleep(10) Test:
10.618 ms, 10.707 ms, 10.689 ms, 10.708 ms, 10.688 ms, Avg = 10.682 ms
Thread.Sleep(5) Test:
5.216 ms, 5.686 ms, 5.812 ms, 5.822 ms, 5.817 ms, Avg = 5.671 ms
Thread.Sleep(1) Test:
1.362 ms, 1.906 ms, 1.915 ms, 1.917 ms, 1.916 ms, Avg = 1.803 ms

IBM ThinkPad X21 PIII-700M Hz XP

Thread.Sleep(25) Test:
24.025 ms, 29.655 ms, 29.990 ms, 30.028 ms, 29.986 ms, Avg = 28.737 ms
Thread.Sleep(15) Test:
19.893 ms, 20.020 ms, 19.994 ms, 19.943 ms, 19.971 ms, Avg = 19.964 ms
Thread.Sleep(10) Test:
9.930 ms, 9.999 ms, 9.983 ms, 10.003 ms, 9.977 ms, Avg = 9.978 ms
Thread.Sleep(5) Test:
9.929 ms, 10.009 ms, 10.112 ms, 9.998 ms, 9.973 ms, Avg = 10.004 ms
Thread.Sleep(1) Test:
9.985 ms, 9.797 ms, 9.990 ms, 10.004 ms, 9.981 ms, Avg = 9.951 ms

XPC SB91P P4 3.2G Vista

Thread.Sleep(25) Test:
24.541 ms, 25.147 ms, 25.260 ms, 25.282 ms, 25.236 ms, Avg = 25.093 ms
Thread.Sleep(15) Test:
15.472 ms, 15.535 ms, 15.531 ms, 15.523 ms, 15.523 ms, Avg = 15.517 ms
Thread.Sleep(10) Test:
10.606 ms, 10.647 ms, 10.607 ms, 10.649 ms, 10.616 ms, Avg = 10.625 ms
Thread.Sleep(5) Test:
5.700 ms, 5.743 ms, 5.819 ms, 5.693 ms, 5.780 ms, Avg = 5.747 ms
Thread.Sleep(1) Test:
1.842 ms, 1.910 ms, 1.884 ms, 1.860 ms, 1.894 ms, Avg = 1.878 ms

HP Proliant DL360 G3 P4 3.4G Win2003

Thread.Sleep(25) Test:
29.770 ms, 30.159 ms, 30.941 ms, 31.194 ms, 31.180 ms, Avg = 30.649 ms
Thread.Sleep(15) Test:
15.682 ms, 15.323 ms, 15.551 ms, 15.604 ms, 15.561 ms, Avg = 15.544 ms
Thread.Sleep(10) Test:
15.458 ms, 15.576 ms, 15.819 ms, 15.373 ms, 15.456 ms, Avg = 15.536 ms
Thread.Sleep(5) Test:
15.454 ms, 15.599 ms, 15.605 ms, 15.468 ms, 15.809 ms, Avg = 15.587 ms
Thread.Sleep(1) Test:
15.269 ms, 15.562 ms, 15.513 ms, 15.559 ms, 15.555 ms, Avg = 15.491 ms

HP Proliant DL360 P-III 1.26G Win 2000

Thread.Sleep(25) Test:
23.968 ms, 31.266 ms, 30.447 ms, 31.165 ms, 31.041 ms, Avg = 29.577 ms
Thread.Sleep(15) Test:
15.682 ms, 15.291 ms, 15.557 ms, 15.521 ms, 15.550 ms, Avg = 15.520 ms
Thread.Sleep(10) Test:
15.486 ms, 15.463 ms, 15.682 ms, 15.418 ms, 15.517 ms, Avg = 15.513 ms
Thread.Sleep(5) Test:
15.472 ms, 15.552 ms, 15.535 ms, 15.569 ms, 15.709 ms, Avg = 15.568 ms
Thread.Sleep(1) Test:
15.290 ms, 15.539 ms, 15.559 ms, 15.551 ms, 15.554 ms, Avg = 15.499 ms

由這個結果來看,Thead.Sleep下去會不會準時起床似乎非關CPU快慢,倒是跟主機板比較有關(例如: 兩台XPC一致,兩台ProLiant Server一致),兩台HP主機,似乎還是卡在無法切出小於15ms的精確度,小黑X21則是努力的要精準,無奈CPU力不從心~~~

只是由這個結論來看,想利用Thread.Sleep更精準控制時間的構想會因機器不同而破功,看來是不可行的,殘念!

Posted 24 March 2007 06:39 PM by Jeffrey | 7 comment(s)
Filed under: ,
TOOLS-HD空間都用到哪裡去了?

玩電腦的人都有一種神奇的本能,不管多大的HD,我們就是本事找來各式各樣,有用沒用的檔案把它塞得滿滿的。沒多久發現空間不足,卻不知道都用到哪裡去了? 

遇到這種狀況,我都習慣仔細分析一下空間的使用狀況,抓出其中的大戶,砍掉兩三個,空間就釋出大半了(80/20原則?)。此時就需要好的工具來分析HD的空間使用狀況,過去我都是用一個免費軟體DiskInfo,它會分析你整個HD的使用狀況,並用美美的圖表呈現。

不過,DiskInfo有兩個小缺點: 1) 每次分析的單位是整顆HD,有時只想看看某個Folder的使用狀況,也得把整顆HD掃完,再一層層鑽進去看。 2) DiskInfo大小排序功能沒寫好,變成用String比較,所以9.12M > 4.00G,orz...

前陣子發現了另一個更酷的Open Source Project--Folder Size For Windows ,安裝之後,直接在檔案總管上可以新增一個顯示欄位叫Folder Size,就可以看到哪裡查到哪裡,十分方便。不過,查Folder Size是很耗資源的,平時開著會拖慢瀏覽資料夾的速度,想知道資料夾大小時再新增Folder Size欄位就好了,平時記得取消顯示,就OK囉。

Posted 23 March 2007 10:03 PM by Jeffrey | no comments
Filed under:
TOOLS-開啟PDF檔應有的速度

好像是從Acrobat Reader 5.0起吧? 我們都已經習慣開啟PDF檔時,一邊聆聽HD嘩啦嘩啦個大半天,一邊看著Splash Screen載入一大票有的沒的,就算只是讀個不到100K的PDF檔,也跟啟動Photoshop這類重兵器沒兩樣,而這段煩人的片頭曲還怎麼都逃不掉。

久而久之,大家似乎也就習以為常了: 反正PDF是Adobe的規格,用Adobe免費提供的Acrobat Reader來讀,天經地義。事實上,我們可以不必這麼委屈,市場上還有更好的選擇! 在Scott Hanselman的Blog發現Foxit Software推出的Foxit Reader 2.0 ,下載後試用了一下,WOW~~ 原來開啟PDF檔可以跟開啟TXT檔一樣快!! 這才是開啟PDF檔應有的速度,我們居然能忍受笨重的Acrobat Reader這麼久?

進一步嘗試,Foxit Reader讀取中文PDF沒有問題,而Scott提到PDF檔太大時會當機的問題,分別開了38M跟23M的大型PDF檔測試,操作起來也都OK。看來,Foxit Reader肯定有資格取代Acrobat Reader,作為Windows預設的PDF檔檢視工具,而我已經這麼做了。

Foxit除了Reader之外,也提供PDF Creator, PDF Editor,對開發者來說,Reader SDK(有DLLActiveX版)也是為自己程式加入PDF功能的選項之一,有興趣的人可以研究看看。

Windows Vista的新設計哲學

自從家機換裝Vista後,每天幾乎都會花幾個小時體驗這個全新的OS。依著以往XP或2003的經驗,卻找不到設定UI是常有的事,舉幾個例子: 設定檔案總管是否顯示副檔名、IIS 7 Web Site的Home Directory、網卡的IP Address設定... 等等都讓我找了好久,感覺上像是不定期會被抓去參加尋寶大賽。

Vista的多項新功能中,最令我滿意的是UAC(User Account Control)! 身為一位專業的網站開發人員,平時使用管理者權限登入Windows操作,也是很合邏輯的。只是具備管理者權限在Windows中操作時,不管病毒、木馬或發狂的程式,很容易就會假藉管理者之名在系統中惡搞。跟系統設計概念一樣,授與超出實際需求的權限是形成資安風險的重要幫凶!

UAC的誔生倒是一舉克服了以往權限無法分割的限制。即使以管理者身份登入Windows Vista,大部分的時間只能使用一般使用者的權限執行程式,當要進行一些管理動作(例如: 開啟電腦管理Console)時會彈出一個畫面,提示你隨後的操作需要用到管理者權限,並請確認是否要放行?  雖然三不五時OS就要跟你Confirm某些事可不可以做,但囉嗦的確認過程卻讓我感到十分安心。舉個例子來說: 萬一不小心開啟了藏有巨集病毒的Word檔,當病毒要企圖改寫Registry以便取得下次開機執行權時,由於更動系統Registry需要管理者權限,我就會被通知要求確認是否放行。如果我的Sense夠,當時也沒有累到精神恍忽,就會發現此一異常現象,不但不會放行,還會追查問題的來源。如此,將可以大大降低被病毒、木馬侵害的風險,I love it!

Vista另一個有意思的地方是記憶體管理哲學的改變! 如果你平時都有觀察記憶體狀況的習慣,Vista"揮雐"的程度,乍看之下可能會讓你心臟病發。例如: 下圖是我關閉了所有程式,執行VS 2005 SP1安裝程式的過程,其中記憶體用量一度飆上1.9G,眼看2G的RAM馬上就要耗盡! 簡簡單單的安裝軟體,將RAM幾乎用光,是傳統Windows User難以想像的事。

用久了之後,你會發現其實每次飆RAM都是有驚無險。Vista只是盡可能用掉所有的RAM,不致有破表之虞,而用掉的RAM事後很快就會歸還(如圖中後半段的陡降)。換個角度想,Vista的記憶體觀念才是對的,昂貴的RAM買來就是要用來增加效能,就算拿來當File Cache也好過放著裡養蚊子。RAM不會因為多用就比較容易壞,留著不用也省不了電,將每一位元都拿來充分利用才是王道呀! Good Job, Vista!

我的雙螢幕天堂路

同事小熊子之前為了要在只能插VGA短卡的DELL電腦上啟用雙螢幕,搞到人仰馬翻。先是短卡的VGA卡很稀少,想插第二張PCI VGA卡卻發生IRQ打架,等到找到VGA短卡又因為DVI-I, DVI-D的問題跑去換;限於機殼空間設計,接頭是用延長的方式偷接出來... 連忙了好幾天、跑了幾趟電子商場,總算才一圓雙螢幕的夢。小熊子戲稱這一路坎坷,有如蛙人弟兄的天堂路...

今年過年把公司的電腦升級成XPC SD37P2,因為是寫程式不是跑GAME,隨便檢了一張最便宜的ELSA 710GS PCI-E卡(AGP的應該更便宜,可惜SD37P2只有兩個PCI-E插槽,本來是要給高檔玩家作SLI圖形加速的),一開始遇到的問題它只有XP/2000的Driver,而裝在我的Windows 2003上,一開機完就會畫面凝結,但系統仍正常,可以用Terminal Service連進去做事。試過ELSA及nVidia的公版Driver都不OK,問了Elsa Support沒回音,在nVidia討論區發文沒人理,最後只好乖乖地裝"標準VGA Driver"將就。

最近在Debug Web時,常常要在VS 2005跟IE間切換,有點煩。於是裝了MaxiVista,用我的NoteBook當延伸桌面,果然除起錯來順暢許多。只是Notebook的螢幕較小(14.1"),比較耗電且佔地方,加上MaxiVista的價格破千,幾乎可以再買一塊VGA,而我的7100GS明明就支援DVI+VGA雙螢幕,為什麼要受這種委屈? 於是再試了一次nVidia公版93.71版Driver ,這次居然可以開起來,還發現Driver中附贈的nView Desktop Manager可以設定水平、垂直跨螢幕或雙螢幕。而要支援雙螢幕,必須重開機時兩台螢幕都接上才會偵測成功。只是VGA接了第二台螢幕,再開機登入OS後就開始發生螢幕週期性全黑閃爍、桌面顯示出現區塊錯亂,沒多久則進入令人抓狂的畫面凍結境界。

不死心,又試了93.81 Beta版,更慘,還沒登入就畫面凍結。之後我又做了N種嘗試,包含了異想天開去找了Trio64V2 DX PCI老爺卡要插(才發現原來PCI與PCI-E是兩碼事)、換插另一條PCI-E Slot、再各重試一次nVidia 91.47, 93.71, 93.81版、Elsa 91.47版,在重開、閃畫面、桌面錯亂、顯示凍結的無間地獄不斷輪迴,肝火上升,即將放棄之際,一次重開,懶得跑顯卡Driver的安裝程式,只用Update Driver的方式更新,沒想到無意中竟把問題給解了。雖然沒有nView可用,但OS的顯示屬性中有兩個螢幕可設定,雙螢幕總算現身!

由這些結果來看,推判我遇到的問題很有可能出在跟Driver一起安裝的程式上(所以我不跑安裝程式,只Update Driver才逃過一刧),只是最近重裝VGA Driver已經裝到快反胃了,也不再有氣力去搞清楚真正的原因。就Case Closed吧!

CommunityServer + ASP.NET AJAX 霸王硬上弓篇

不知有沒有人試過在Community Servery 2.1上加掛ASP.NET AJAX?

原則上,只要利用之前提過的方法修改Community Server的web.config檔,接著試著新增一個WebForm1.aspx,拉拉ScriptManager,UpdatePanel,Label,Button的馬上就可以體驗到在Community Server網站"新增"ASP.NET AJAX網頁的快感! 有沒有留意到我強調"新增"? 重點來了,就在初試成功,想要快快樂樂地為Blog首頁也加上各式AJAX特效時,不久會被"Extender controls may not be registered after PreRender."錯誤訊息電到手腳發軟~~~

在Community Server, ASP.NET AJAX與AJAX Control Toolkit的Souce Code裡追了幾個晚上,我大概了解其中的祕辛。Community Server引進了一套Skin/Theme的概念,也就是如你現在所看到的Blog頁面,其實是由一個個.ASCX所拼湊而成的,而且可以隨意切換。舉例來說,我們可以為"最新回應"清單設計出兩個不同的ASCX,後端的元件只負責由資料庫中取出資料,至於前端要用Repeater或DataGrid來呈現,完全由ASCX決定。這種做法提供了極大的客製化彈性,但背後乃是透過一些幾近Hacking的手法完成的,其中一個重要環節便是巧妙地override了Render、PreRender這些事件!  無奈英雄所見略同,ASP.NET AJAX也要依懶PreRender來達成輕輕鬆鬆將原有元件升級的神奇魔法,於是二者在Control的生命週期假設上有了衝突...  說來話長,總結一句,要解決這個衝突,得去更動二者其中之一的核心架構,會產生多大的後遺症無法評估...

可是,你也許注意到了,現在Blog右方的導覽SideBar可以動態開合,這不就是ASP.NET AJAX Control Toolkit中的Accordion嗎? 明明就說不能整合了,這又是怎麼做到的呢?

對由ASP.NET AJAX入門的人來說,AJAX是透過拖拖拉拉Web Control,寫寫Server-Side Event達成的;而在JavaScript老鳥的眼裡,ASP.NET AJAX只是一群好心的Developer為了讓不會Javascript的初心者也能使用AJAX的善心義舉,說穿了還不就是靠Javascript+DOM在表演。我用的賤招是先做一個Accordion的網頁,查出它用了哪些js,手工蒐集下來,一一在我的ASCX中用<script src="..">包含進來,AJAX Control Toolkit的設計很棒,絕大部分複雜的邏輯都在.js中,網頁中只需要再加一兩行Javascript就可以完成預期的效果。就這樣,明明不能相容的兩個東西,就這樣硬是整在一起了,Kero Kero Kero Kero....

不過,如果有人找到可以真的在Community Server Skin ASCX中使用AJAX Control的方法,記得跟我說,我會幫忙寫一篇"CommunityServer + ASP.NET AJAX 明媒正娶篇"。

TIPS-TransactionScope過三關

.NET新推出的TransactionScope提供了更簡便封裝Transaction的寫法。今天試著從我的Windows 2003 用TransactionScope包裝一段對SQL 2005 @ Windows 2000的程式碼時,卻連闖三關才達陣!

首先,我收到這個錯誤:

System.Runtime.InteropServices.COMException (0x8004D024): The transaction manager has disabled its support for remote/network transactions. (Exception from HRESULT: 0x8004D024)
Network access for Distributed Transaction Manager (MSDTC) has been disabled. Please enable DTC for network access in the security configuration for MSDTC using the Component Services Administrative tool.

後來發現是因為Windows 2003(DB的Client端)的"Enable network DTC access"安裝選項沒開啟(以往都只注意到SQL 2005 Server上要啟用這個功能,今天才確認了Client端也要設定),依在Windows 2003上啟用MSDTC的SOP處理完畢,卻忘了開防火牆給MSDTC.EXE,結果得到以下錯誤:

Error HRESULT E_FAIL has been returned from a call to a COM component.
Communication with the underlying transaction manager has failed.

在防火牆上開放MSDTC.EXE通行後,卻還是同樣的訊息。這才想起: "兩台機器分屬不同網域!"。用了之前的跨網域DTC設定技巧,連闖三關,總算才測通。

今天這篇TIPS算是給大家來個MSDTC設定與錯誤排除總複習吧!

TIPS-AJAX Control Toolkit CascadingDropDown

要學會使用CascadingDropDown,建議先看過Sample網站所附網頁的Source Code。而在嘗試的過程中,可能會遇到些小問題,以下就列出幾處讓我跌倒的地方:

  1. 如果你想借用Sample中的QuerySimpleCascadingDropDownDocument,XML NodeName記得用小寫。
    因為Source Code中有這行"xpath += ("/" + category.ToLowerInvariant());",害我追了好久。
  2. 在測試CascadingDropDown時,你可能會不時遇到以下的錯誤,多半發生在由其他Control觸發PostBack時。原則上只要將Page的EnableEventValidation設為False就OK了。

    Invalid postback or callback argument.  Event validation is enabled using <pages enableEventValidation="true"/> in configuration or <%@ Page EnableEventValidation="true" %> in a page.  For security purposes, this feature verifies that arguments to postback or callback events originate from the server control that originally rendered them.  If the data is valid and expected, use the ClientScriptManager.RegisterForEventValidation method in order to register the postback or callback data for validation.

    【技術細節】EnableEventValidation主要是用來防止Injection攻擊。例如: 有個DropDownList明明只有user1, user2, user 3三個選項,但被人用Client Side Script增加了第四個選項administrator,EnableEventValidation就可以攔下這種變造的攻擊。不過,CascadingDropDown看來也在做"變造"下拉選項的工作,因此也被攔了下來,要讓動態改變的選項值合法化的步驟挺麻煩的,個人認為可行性不高,所以針對特定網頁在Page中將EnableEventValidation停用似乎較合理。(這種問題打打小洞就好了,千萬不要自暴自棄地在web.config中停用全網站的Event Validation) 如果有興趣對這個議題多一些了解,這裡有一篇好文章

  3. WebMethod似乎會被Server端Cache住,有幾次經驗,明明WebMethod的內容已經修改,但CascadingDropDown取回的卻一直是修改後的值,即使我用IE Dev Helper Toolbar Disable Cache或是加上亂數QueryString都是如此。最後得靠重啟Web Application(不一定要IISRESET,將web.config重新存檔即可)才解決。
  4. WebMethod的Code是以static method的方式存在,除了傳入的參數,沒辦法與呼叫端的ASPX溝通,我用了點小技巧,透過HttpContext.Current.Request.UrlReferrer取得呼叫網頁的URL,可以解決了一部份需求。
KB-由ASP.NET 1.1昇級的網站無法啟用MS AJAX

上次介紹過如何修改web.config在現有的ASP.NET專案上啟用MS AJAX[註: MS AJAX官方網站上有對各Config Section的詳細說明],我已經用同樣的方法成功升級過好幾個專案,不過今天被電了一下。有個專案無論我怎麼調web.config,MS AJAX就是不生效! 即使Button在Update Panel中,Click也會觸發PostBack。

查了好久,總算找出原因: 由ASP.NET 1.1升級的專案,web.config中會有<xhtmlConformance mode="Legacy"/>的設定,目的在讓ASP.NET 1.1時代建立的非XHTML格式網頁能正常顯示。但它卻會影響MS AJAX的正常運作,用Fiddler比對的結果,加入xhtmlConformance 設定後,原本在網頁HTML中應該要載入三個JS,只會剩一個,而這些JS就是處理Validation、Postback等邏輯的Client Script,少了AJAX功能就會失常!

<script src="http://blog.darkthread.net/MiniAjaxLab/WebResource.axd?d=txk52LiGXXTyXT-eNSvsSQ2&amp;t=633076449793593750" type="text/javascript"></script>
<script src="http://blog.darkthread.net/MiniAjaxLab/ScriptResource.axd?d=opollxqh..略...p_Ag1&amp;t=633076458731093750" type="text/javascript"></script>
<script src="http://blog.darkthread.net/MiniAjaxLab/ScriptResource.axd?d=opollxqh...略...9ffhog0&amp;t=633076458731093750" type="text/javascript"></script>

順道補充一點,熟悉ASP.NET 1.1的人會發現以前放在aspnet_client\system_web\1_1_4322下的webuivalidation.js等檔案,在ASP.NET 2.0中已經找不到了! ASP.NET 2.0導入了webresource.axd的觀念,JS、圖檔可以Embedded進DLL中,再透過WebResource.axd?d=SbXSD3uTnhYsKUOZ6IXYG8QCXW86UizF0&t=632768953157700078的URL動態由DLL中取出傳回Client端,如此,就不必為了額外部署Client端程式而困擾囉! 這招在開發自訂控件(Custom Control)時,十分好用!

ThreadAbortException When Response.End()

以下這段Code,如果getIdFromDb()傳回"1"時,顯示結果為何?

是"ID=1"嗎? 錯!! 後方還會接上"ERROR:Thread was being aborted."

   15 protected void Page_Load(object sender, EventArgs e)

   16 {

   17     try

   18     {

   19         string id = getIdFromDb();

   20         if (id != null)

   21         {

   22             Response.Write("ID=" + id);

   23             Response.End();

   24         }

   25     }

   26     catch (Exception ex)

   27     {

   28         Response.Write("<br>ERROR:" + ex.Message);

   29         Response.End();

   30     }

   31     //Do something when id not found

   32     //....

   33 }

我寫了一個元件,其中有個Complete() Method會直接輸出XML,為了防止後方又被呼叫方Response.Write多餘的文字破壞XML結構,我在Method中呼叫了HttpContext.Current.Reponse.End(),結果呼叫端用try...catch包住Complete(),即使正常執行完成,卻老會在Exception中觸發ThreadAbortException。

Goggle了一下,才發現我並不真的了解Response.End。依據微軟KB 312629的說明,Response.End、Server.Transfer、Response.Redirect被呼叫時,本來就會觸發此一ThreadAbortException(我倒是不理解為何正常的執行流程,卻要夾帶一個Exception,某非是某個SD人員的鋸箭之舉),因此若在try ... catch中包含了這三個執行指令,就會因為ThreadAbortException而跑入catch區段。

依KB的說法,要解決方法包含: 1) 用HttpContext.Current.ApplicationInstance.CompleteRequest();取代Response.End(); 2) 如果是Server.Transfer,可用Server.Execute代替 3) 考慮改用Response.Redirect("...", false) (有程式會繼續往下跑的後遺症)

我還想到的另一種方法是try ... catch時去catch ThreadAbortException後Do Nothing,只是這犯了把Exception當正常邏輯的效能禁忌,所以不應在考量之列。

KB-Report Margin Changed After SQL Server 2000 Report Service SP2

最近有台SQL Server 2000 Reporting Service主機在部署好報表後,才發現忘了上SP2。上完SP2,卻發現部分報表在列印時邊界設定變了,導致原本一頁要印完的報表印成兩頁。

Google了一下,找到這篇說明,原來SP2為了配合線上列印的Print Control,會在Publish時為報表補上Page Size, Margin等屬性設定,但在SP2之前Publish的報表沒有這些屬性,就會套用8.5" x 11"的紙張, 0.5"邊界的預設值,造成列印結果與原先設計有所出入。這個問題只要重新上傳Publish報表就可以解決,但如果已有成百上千張報表,全部Republish會讓人手軟吧!

該篇文章提供了一段Reporting Service Script(我又學到新東西了),可以透過Script一次Republish所有的報表:

Public Sub Main()
    Dim definition As [Byte]() = Nothing
    Dim item As CatalogItem = Nothing
    Dim items() As CatalogItem = Nothing

    rs.Credentials = System.Net.CredentialCache.DefaultCredentials

    items = rs.ListChildren("/", True)

    For Each item In items
        If item.Type = ItemTypeEnum.Report Then
            Console.WriteLine("Republishing Report: {0}", item.Path)
            definition = rs.GetReportDefinition(item.Path)
            rs.SetReportDefinition(item.Path, definition)
        End If
    Next
End Sub

很多人應該跟我一樣,對Reporting Service Script Host很陌生。所以補充一下,執行的步驟是將以上的Code存成Republish.rss檔案,然後在安裝Reporting Service的主機,執行以下指令:

rs -i republish.rss -s http://locahost/reportserver

大功告成!

PS: 對Reprting Service Script Host有興趣的人,可以參考這篇文章

More Posts Next page »

Search

Go

<March 2007>
SunMonTueWedThuFriSat
25262728123
45678910
11121314151617
18192021222324
25262728293031
1234567
 
RSS
【工商服務】


BlogLook Score and Rank

Syndication