January 2008 - Posts

我的第一支Linq小品

VS2008裡最革命性的改變應該就是Linq,雖然已改用VS2008好幾個月,但都是在趕些急如星火的ASP.NET 2.0專案,因此到目前為止只享受到Javascript Intellisense等周邊的IDE改良。

今天喘口氣,切換去處理另一個任務,裡面剛好有個需求,要寫個小工具做批次資料轉換。由於是一次性作業,平台語言可完全自訂,加上程式邏輯簡單,這種機會不拿來練功,下次不知要等到民國幾年。設好停損點(註: 如果多久沒搞定就放棄,改回用.NET 2.0寫),捲起袖子寫了我第一支應用Linq的小品程式。

情境如下: 資料來源為一隻getdata.asp,傳入不同的QueryString(?id=nnnn)可以逐筆取回資料,資料為XML格式,共有多個Group,每個Group有筆數不一的Row,Row的ItemID為為識別值。

<?xml version="1.0" encoding="utf-8"?>
<Result>
  <Group GroupID="群組1">
    <Row ItemID="02330BP" />
    <Row ItemID="52821BP" />
    <Row ItemID="10941BP" />
  </Group>
  <Group GroupID="群組2">
    <Row ItemID="03001BP" />
    <Row ItemID="60041BP" />
    <Row ItemID="20131BP" />
  </Group>
  <Group GroupID="群組3">
    <Row ItemID="90231BP" />
    <Row ItemID="10011BP" />
  </Group>
</Result>

由XML中取出Group跟Row的過程,用XmlDocument就可以輕鬆搞定,但這回我打算用Linq to XML來處理,算是練功。

Linq的寫法頗特殊,要花點時間熟悉,ScottGu寫了一篇很棒的Ling to XML tutorial,光靠著這篇Tutorial,居然就把程式寫完了。Check it out!

//宣告群組資料結構,用一個List<string>來存群組成員ItemID
public class ItemGroup
{
    public string Name;
    public List<string> Items;
}
 
static void Main(string[] args)
{
    string id = "1234";
    string getUrl = "http://remote_server/getdata.asp?id=" + id;
    
    //由ASP取回XML,做為XDocument的來源
    XDocument xd = XDocument.Load(getUrl);
    
    //由於要列出Group由Row,第一次就得用上SubQuery的技巧,真"硬斗"... orz 
    var groups = from groupNode in xd.Descendants("Group")
                 select new ItemGroup
                 {
                     Name = groupNode.Attribute("GroupID").Value,
                     Items = 
                            (
              from record in groupNode.Elements("Row")
                             select record.Attribute("ItemID").Value
                             ).ToList<string>()
                 };
    
    //用兩層foreach列出所有群組及成員
    foreach (ItemGroup grp in groups) 
    {
        Console.WriteLine("Name=" + grp.Name);
        foreach (string item in grp.Items) 
            Console.WriteLine(item);
    }
}
我的志願

小時候,我的志顯跟一般的小孩沒有兩樣,崇拜著愛迪生、牛頓,長大想當科學家,幻想自己研究出改寫人類歷史的驚世發明。

漸漸地,年紀愈大、志願愈小,踏出校園的那一刻,腦中殘留的小小企盼只剩下能找份穩當的工作,上班下班,結婚生子,了此殘生... (援引二技同學李大雄的名言,十幾年來咀嚼再三,餘味無窮)

剛過完生日,才驚覺,明明自己不久前還是個稚嫩的青澀少年,怎麼轉眼就變成年屆不惑的糟老頭? (說到這裡,腦海中迴盪著陳松勇那句豪邁沙啞的藥酒廣告台詞: 相公敬俠丟嘸ㄌㄧㄚ 四十歲夠甘那一尾活龍~~ 活龍~~ 活龍~~~~)

算了算,自己堪稱是個幸福的中年人,沒什麼發財命,但總也衣食無缺(這年頭舉凡媒體、廠商、政客到菜市場的歐巴桑,講到這類話題,最後的結論都只有一個--->泛濫到令人想吐的"M型社會",噁~~ 若依此標準,我該不會躺在中央的谷底吧?),一雙兒女吵歸吵,卻也健康活潑,再嫌東嫌西遲早要遭天譴。

但是,成天周旋在工作與生活間轉呀轉,不如意、不順心的惱人時刻總是有的。以前的我不懂選擇,如果還有機會,這次我想當個----派大星!

真是愛死這個傻呼呼的笨傢伙,不用工作(海綿寶寶、章魚哥至少還得在蟹堡王打工賺錢)、不會累(永遠精神奕奕,你有看過他玩到體力耗盡嗎?)、不怕死(不管切成兩半、炸成碎片、壓得扁肩,下一秒又是活生生的),沒有心機(比起來,蟹老板跟章魚哥簡直奸巧世故到令人生厭)、沒有煩惱(好像沒看過他為什麼事擔過心、發過愁),真是羨煞人的三不二沒有呀!!

【圖說】女兒知道我鍾愛派大星,前些時候從健達出奇蛋裡挖到一隻,就打定主意送給我當生日禮物。她"大聲"地跟媽媽要包裝紙跟紙盒包禮物,還親手剪了色紙做小緞帶,還"高聲"跟媽媽討論今年要給我一個生日驚喜。從頭到尾聽得一清二楚的我,生日當天還是佷捧場地故做驚喜狀,口口聲聲誇說這是我收過最棒的禮物,沒想到平時倔強得像塊花崗岩的凶巴巴小女孩,這回竟害臊地躲到我背後去不敢看我,好可愛!

【茶包射手專欄】超愛開新Tab的IE7

小問題一個! 換了IE7之後,我發現每次在Address Bar輸入新網址,IE7就會新增一個Tab開啟新的URL。我習慣用blah.asp?x=1, blah.aspx?x=2(x是隨便輸入且不具任何效用的參數,只是為了讓URL不完全相同)的手法來確保不會讀到Cache中的內容,在IE7上每換一次參數就生出一個新Tab,一番測試下來,就累積了數十個Tab,有點小煩... (謎之聲: 一個測試反覆跑這麼多次,看來你的程式能力也不過爾爾)

Google了一下,意外學到不少IE7 Tips,其中提到輸入URL後按Alt-Enter可以在新的Tab中開啟URL,而倒沒看到有人回報會被強迫開新Tab的困擾。只是有不少的Trouble-Shooting Tips都提及,IE有不少奇怪行常是IE Add-On引起的,所以我決定朝此方向偵辦。

講到這個,就要順道提一下IE7一個好用的新功能:

我們都知道Windows有所謂的安全模式,可以避開部分因驅動程式、應用軟體造成無法開機的情況,Add-On常是造成IE7發瘋的原因,所以IE7也有類似的"安全模式"。在"程式集/附屬應用程式/系統工具"裡有個"Internet Explorer(沒有附加元件)",可以讓IE7停用全部的Add-On再啟動。

試了一下,果然停用Add-On後,輸入新URL就不會開在新Tab中,所以現在可以確認這個行為是某個Add-On引起的。利用IE選單中的"工具/管理附加元件/啟用或停用附加元件"逐一關閉Add-On做測試,我找到了IE7Pro。再深入IE7Pro的選項,在Base Setting中看到"Open new Tab from address bar"被勾選了,宣告破案! 收隊!

KB-About Error Updating JScript Intellisense

Javascript Intellisense是吸引我改用VS2008編輯ASP.NET 2.0專案的重要原因之一,我卻被一個煩人的相關警告騷擾了好幾天。

只要一Build專案,Error List中就會出現Error updating JScript IntelliSense: MicrosoftAjax.debug.js:: 'Sys.Res.argumentDomElement' is null or not an object @ 201:8的警告訊息,發生地點在C:\WWW\MyWeb\Default.aspx,Double Click警告,會跳到Default.aspx的第一列,表面上看不出有任何關聯。

摸了好久才搞清楚是怎麼一回事,原來這與Javascript Intellisense的運作原理有關。狀況不太容易說明清楚,所以用一個簡單的實例示範,待我細細道來...

我原先寫了類似以下範例,用$addHandler掛載onclick事件的Javascript Code:

<input type="button" id="btn" value="Click Me!" />
<script type="text/javascript">
$addHandler($get("btn"), "click", btnClick);
function btnClick() {
    alert("Yo!");
}
</script>

後來,發現這段Code可以給多個網頁共用,所以很直覺地把它拆出來另存成inc.js:

$addHandler($get("btn"), "click", btnClick);
function btnClick() {
    alert("Yo!");
}

然後Default.aspx改用Inclue:

<input type="button" id="btn" value="Click Me!" />
<script type="text/javascript" src="js/inc.js"></script>

這種改法在過去ASP/ASP.NET時代玩過千百回,這次也不例外,實際執行沒發生任何差錯。只是從此以後,只要我一Build網站或企圖Debug,Error List就會跑出Error updating JScript Intellisense的警告,雖然並不影響網站運作,但一來這個警告對有潔癖的我來說頗為礙眼,二來出現這個警告後,VS2008就不會對inc.js裡的其他函數提供Javascript Intellisense功能。逼得我心不甘情不願地花了點時間反覆測試與追查,總算理出一些頭緒:

  1. 為了產生Javascript Intellisense的神奇效果,VS2008在你Coding的階段就會即時執行網頁與js檔裡的Javascript, 'Sys.Res.argumentDomElement' is null or not an object的錯誤便是VS2008呼叫$addHandler時抛出的參數無效例外。
  2. 當VS2008單獨執行inc.js裡的$get("btn"),當下沒有任何網頁DOM,想當然爾會傳回null, 接著null被當成$addHandler的第一個參數,觸發了前述的錯誤。
  3. 當VS2008無法執行、分析js,便會傳回Error updating JScript Intellisense警告,並喪失部分提示能力。

搞清楚原委,我們要進行的處理便是要確保inc.js可以被"獨立執行"而不出錯,$get("btn")的寫法必須跟網頁一起執行才有意義,單獨跑inc.js時便會出問題。因此我把$addHandler包進一個initJob()函數,再由網頁中呼叫initJob(),這樣便可確保在每次被呼叫的當下,$get("btn")都會傳回正確的物件。

所以,inc.js修改如下:

function initJob() 
{
    $addHandler($get("btn"), "click", btnClick);
}
function btnClick() {
    alert("Yo!");
}

Default.aspx則加上呼叫initJob()的Code:

<input type="button" id="btn" value="Click Me!" />
<script type="text/javascript" src="js/inc.js"></script>
<script type="text/javascript">
initJob();
</script>

搞定收工!

TIPS-IE Operation Aborted Error Again
<%@ Page Language="C#" AutoEventWireup="true" %>
<html>
<head><title></title></head>
<body>
    <form id="form1" runat="server">
    <script type="text/javascript">
    var d = document.createElement("div");
    document.body.appendChild(d);
    </script>
    </form>
</body>
</html>

寫了如上的Code,又被Operation Aborted的Error搞得七葷八素...

之前雖然有寫過類似的TIP,不過顯然當時的結論不夠精確。我並沒有將Code放在Table中間,但一樣會發生問題。這回我倒是Google到了MS的官方文件,搞清楚了問題的根源: (依照KB的說法,它是個Bug,卻歷經IE5.5->IE6->IE7,代代相傳,不簡單呀!)

This problem occurs because a child container HTML element contains script code that tries to modify the parent container element of the child container.

原來在<div></div>裡執行document.body.appendChild就算是變更Parent的內容,並不是我之前所認知的<table></table>。雖然在我的程式中沒有出現<div></div>,但是ASP.NET在產生form1的HTML Code時會自動用一個div將所有的表單內容物都包起來,就符合了在<div></div>中appendChild的情境而導致錯誤。

知道了原因,要解決很簡單,將<script></script>移到</form>之後即可,收工!
發現一件悲慘的事,我真正要應用的場合有套用MasterPage,<form>被放在MasterPage那一層,因此在網頁裡不可能將Code放到</form>之後... orz

想了一下,找到了個法子,既然在<div>中動手不成,所以我就改成:
window.setTimeout('document.body.appendChild(d);',1);

薑!薑!薑!薑... 搞定收工! 這次是真的。

當心! IE7要全面更新囉

微軟已經正式宣告,基於強化瀏覽器的安全性,將於2008/02/12起將透過自動更新機制強制升級至IE7(就是只要做Windows Update, 預設就會升級到IE7)。

對一般使用者來說,IE7提供更好的安全防護,例如: IE7上線後每週就至少為User擋下90萬人次的釣魚網站詐騙,內建Tab的功能也是很方便的改善。不過經驗裡,IE7運作需要的Resource較IE6為多,CPU/RAM的使用率會上升,對一些本來就是拄拐扙才跑得動IE6的阿公級電腦來說,會是項新挑戰。

以上是由使用者的觀點看待本次的IE7全面升級;對網管人員來說,有些WSUS或部署頻寬要考量,只要事前妥善規劃,問題也不大;但對網頁設計者來說,恐怕就沒這麼輕鬆愉快了。

由於IE7與IE6有一些行為上的改變(我自己就發現過好幾個: Tip1, Tip2, Tip3),使用者由IE6升級到IE7後,有些網頁設計方式可能會發生不相容的問題。對於Internet Web Site的網站設計者來說,IE7已問市一年多,不相容的問題早已陸續反應,更不用說還得考慮對FireFox的支援,預估此波全面IE7化的衝擊有限。但一些企業內部網站,可能還把瀏覽器版本限定在IE6上,那麼一旦2/12全部改換IE7,搞不好隔天因相容問題搞到全網站停擺,就是會出人命的大事了! 提醒一下,如果你的Web Site目前只支援IE6,快趁著這段時間檢視修改,確保它能與IE7相容。

如果差異太大來不及改或是想再當一陣子鴕鳥的人,MS也有建了一座象牙塔,可以讓暫時不想面對IE7的人避難。以下的工具可以防止IE7以強制方式(高優先)自動更新,但仍允許使用者自行選取IE7更新,對於嚴格控管Client Browser的企業來說,應該可以暫時避開02/12被迫全部升級IE7的問題。

Toolkit to Disable Automatic Delivery of Internet Explorer 7 http://www.microsoft.com/downloads/details.aspx?FamilyId=4516A6F7-5D44-482B-9DBD-869B4A90159C&displaylang=en

不過,就長遠來看,該來的躲不掉,出來混,遲早要還的,還是早早修校網站,解決IE7相容問題才是上策。

KB-MasterPage ClientID Search Enhancement

先前曾提出在MasterPage中解決ClientID被加料的方法,最近發現原來的做法有些小缺點。

原先的邏輯只額外多搜尋ContentPlaceHolder的字頭(Prefix),但是若其中有UserControl,則UserControl的ClientID還會被加上UserControl的ID。例如: (UC11中只有一個TextBox1)

<asp:Content ID="Content2" ContentPlaceHolderID="Editor" Runat="Server">
    <uc1:UC1 ID="UC11" runat="server" />
</asp:Content>

實際的TextBox1的ClientID會變成ctl00_Editor_UC11_TextBox1,原先的做法只會Search到ctl00_Editor_TextBox1,因此UserControl下的物件就變成化外之民。

<input name="ctl00$Editor$UC11$TextBox1" type="text" id="ctl00_Editor_UC11_TextBox1" />

我修改了原有的程式,將UserControl的ClientID也加入要搜索的字頭之一,同時對ContentPlaceHolder下的Control也要逐一巡迴檢查,這樣就能把UserControl下的物件納入afa_mpget的查找範圍內。

    public static void searchContentPlaceHolder(Control ctrl, List<string> lst)
    {
        if (ctrl is ContentPlaceHolder || ctrl is UserControl)
            lst.Add(ctrl.ClientID);
        if (ctrl.HasControls())
        {
            foreach (Control c in ctrl.Controls)
                searchContentPlaceHolder(c, lst);
        }
    }

只是,原先與網友大估討論過將邏輯放在Page_Init解決對ViewState干擾的做法,由於在搜尋的時候必須確認網頁上的Control物件都已Create妥當,以免在列舉時有遺珠,因此放在Page_Load()比較OK。最終決定的做法: 放在Page_Load()+Page.Header.Controls.Add(...)。

Unit Test Reference Issue

在Visual Studio中跑單元測試,遇到以下訊息:

Failed to queue test run 'jeffrey @MyMachine 2008-01-17 09:31:03': Test Run deployment issue: The location of the file or directory 'R:\Assembly\Utility.dll' is not trusted.

訊息很明確,R:是一台網路磁碟機,預設來自非本機的.NET Code在使用上會被設限。要解決也很簡單,透過"Microsoft .NET Framework 2.0 Configuration"配置工具新增一條URL Code Group指向file: //\\ShareServerName\SharedFolder\*即可,詳細的說明可以參考MSDN:

英文: http://msdn2.microsoft.com/en-us/library/bs2bkwxc(VS.80).aspx
中文: http://msdn2.microsoft.com/zh-tw/library/bs2bkwxc(VS.80).aspx

TIPS-Escape { } in String.Format

一個很小很小的問題,只是以前都很鄉愿地繞路避開,今天終於肯花時間找到正解:

static void StringFormatTest(string custCode)
{
    string s = string.Format(@"
function blah() {
    {0}
}", custCode);
    Console.Write(s);
}

以上的寫法,在執行時會跑出System.FormatException: Input string was not in a correct format.(輸入字串格式不正確。)  原因很清楚,{與}在格式化表示時是用來包數字標註變數內容的位置,而它恰好與Javascript的Block符號相同,造成誤解。隨便Google了一下,用string.format+escape當關鍵字,一下就找到解答(很奇怪過去為什麼都沒花心思去找?)。在String.Format中,{{代表{、}}代表},就跟@"...."中表示雙引號要重覆打兩次一樣,That's all, folks.

static void StringFormatTest(string custCode)
{
    string s = string.Format(@"
function blah() {{
    {0}
}}", custCode);
    Console.Write(s);
}

有用的參考資料: http://blogs.msdn.com/brada/archive/2004/01/14/58851.aspx

PS: 順便做一下資安宣導,以上的寫法若custCode是由使用者自由輸入時,會有XSS的風險,相關文章: 參考1/參考2

連一拉一

收到一只DHL快遞包裹。

咦... 是誰送禮物給我?

打開箱子,裡面是一只盒子,取出盒子時看到一位宅男對我傻笑型男對我微笑...

取出盒子,封面上斗大的MVP字樣,揉揉眼睛,莫非?

西滴! 微軟感念不服老的中年人過去一年拼了老命在Blog上講得口沬橫飛、寫了超過200篇自以為很珍貴的技術文章、還很雞婆地回答網友的問題... 念在沒有功勞也有苦勞,沒有苦勞也很疲勞(很不少Post是在凌晨或清晨時分瞇著眼寫完的,可謂字字血淚...orz 無論如何,疲勞是真的。),微軟佛心大發,特別讓中年人連任MVP一年,這是特地奉上的MVP寶盒。

今年有一枚金屬浮雕胸章...(不知與大和拜金女裡的馬主別針有沒有得拼?)

接著來看最讓人期待的Geek禮品! 去年MVP寶盒中有皮質名片夾,1G行動碟跟簡報雷射筆的紀念禮,今年的禮物藏在中間的方盒中...

是藍牙裝備組! 有藍牙USB Dongle(說明書中叫它USB加密狗)、可以插進PCMCIA插槽的藍牙滑鼠(那麼扁,會好用嗎?)、藍牙耳機以及藍牙音源發射器(附一個耳機接頭,可將音源送到耳機裡),看起來很不錯哩。

兒子說: "有個一天到晚打電腦,抽不出時間陪我玩的把拔,搶走他MVP禮品的第一手試用權作只是剛好而已。這是一定要的啦!"

(1/1接到微軟通知,勤奮無畏的中年人連任MVP成功! 不過基於有圖有真相的原則,還是乖乖等拿到MVP寶盒才公告這個消息。沒錢敗家,所以沒寫過開箱炫耀文,難得有機會拿到別人買不到的東西,就用這個寫寫開箱文過乾癮吧!)

BULK INSERT Performance

一直以為BULK INSERT就不會記Log,結果今天跌了一跤...

我下的語法如下(錯誤示範,小朋友不要學)

TRUNCATE TABLE RESD

BULK INSERT RESDE
FROM 'C:\DataProc\Output\RESD.txt'
WITH
(
    FIELDTERMINATOR = '\t',
    ROWTERMINATOR = '\n'
)

RESD.txt約1.4G,結果BULK INSERT花了30分鐘還沒做完,Log檔就長到20G,把HD空間給吃光。急忙Cancel,但依DB的運作,此時SQL會將剛才未做完的INSERT Rollback回去,20G的Log! 等了40分鐘還等不到Cancel完成。

請教了DBA,DBA認為剛才有HD空間不足的變數,SQL Server 2005可能已經不正常,建議我重新啟動SQL試試。重新啟動SQL後,順道見識了SQL 2005的新功能,SQL還是會將剛才沒做完的Rollback繼續做完,此時正在Rollback的DB會顯示(in recovery)而暫時不能用,但其他的DB則Ready了,花了約兩分鐘,in recovery的DB也回到可用狀態。(SQL 2000需要等Rollback做完,整台Server才能用)

在苦等DB Rollback的過程裡,我Google了一下,發現幾件事:

  1. Nonlogged BULK INSERT只有在一些條件下才成立(我一直以為BULK INSERT==No Log):
    - DB選項必須開啟SELECT INTO/BULK INSERT
    - 不可設Index;如果有,開始BULK INSERT時,TABLE必須是空的
    - 需加上TABLOCK提示
    - 資料表沒有設定複寫(Replication)
    http://www.mssqlcity.com/Tips/bulk_copy_optimization.htm
  2. 由於誤認BULK INSERT不會有Log Issue、加上將DB設成Simple Recovery Mode(Truncate Log At Checkpoint),我沒意會到讓2.5億筆資料變成一個Transaction是多可怕的事。因此才會爆出可怕的Log量,其實我只要透過BATCHSIZE=1000,讓BULK INSERT過程中每1000筆Commit一次,Log量就會小很多了。不過資料匯入後,不過2G,Index也只有一組,我還是無法理解為什麼可以產生10倍大小的Log?

綜合以上,BULK INSERT SQL可以改成:

BULK INSERT RESDE
FROM 'C:\DataProc\Output\RESD.txt'
WITH
(
    BATCHSIZE = 1000,
    FIELDTERMINATOR = '\t',
    ROWTERMINATOR = '\n',
    TABLOCK
)

Smart C# Compiler

前陣子幫同事追查問題,由於懷疑主機上的程式版本有誤,便找來Source Code,與Reflector反編譯(Decompile)主機上DLL得到的Code比較,在一段程式上發現了小小的差異: Souce Code裡是先將DropDownList的SelectedValue先存到變數中,再將該變數當成呼叫另一個函數的參數;而Reflector中看到的卻是直接將DropDownList.SelectedValue直接被當成呼叫另一函數的參數。

程式寫法的差異,讓人懷疑上線的程式版本有誤。但同事印象中從頭到尾不曾用過這種寫法,追查上線記錄則顯示版本管理出錯的機率極低,到底是怎麼一回事? 幾經波折,最後在其他地方找到錯誤的根源,與程式邏輯無關,證實了程式版本並沒有任何問題。但引發一個有趣的結論: "DLL反編譯的結果與Source Code可能存在差異",之前在探討Nullable Value Type時,曾有類似的經驗,莫非這次又遇到?

一些效能討論文章中提過Compiler本身會做一些最佳化,剛才提到Source Code將DropDownList.SelectedValue存成變數,變數只被用了一次--當成呼叫函數時的參數。少了這個變數不會對邏輯有任何影響,還可以省下宣告/建立變數的成本,的確是種最佳化。

為了印證這點,我寫了以下的程式,還順道驗證一下上回提過的靜態字串相加會直接被Compiler做掉的理論,程式如下:

using System;
using System.Collections.Generic;
using System.Text;
 
namespace SmartCompiler
{
    class Program
    {
        static void Main(string[] args)
        {
            int j = 0;
            string s = "Hello " + "World!";
            string t = s;
            Console.Write(t);
            int i = 3 + 5;
            Console.Write(i);
        }
    }
}

用Reflector解析一下,有幾個地方被"最佳化"了:

1.變數j宣告了卻從未用到,Compiler直接忽略,在DLL中完全未現身。
2.由於s只被用來指定給t,所以就直接用t取代s。
3."Hello World!"直接變成一個字串指定給t。
4.Compiler直接算好3+5=8指定給i。

Compiler真是愈來愈聰明了,不過不代表以後程式可以亂寫,再交給Compiler去調。Compiler能做的小修正有限,省下來多半只是幾個Tick的極微時間,有時數百萬個Compiler最佳化也抵不上Coding時的一個小疏忽,只能當成錦上添花。

講到Coding時應注意效能,最近又再度見識到在上萬次迴圈中反覆做Database查詢的"好"程式,一整個囧!

KB-J avascript: Is Not Allowed In HyperLinkField!

今天才發現這點。我在ASP.NET 2.0的GridView中想要放一個HyperLinkField觸發Javascript Function,但一在DataNavigateUrlFormatString中加上"j avascript:"字眼,產出的HTML裡,該Link會完全消失,只剩下DataTextField的純文字。

<asp:HyperLinkField DataNavigateUrlFields="PRODID,PRODCHNAME" 
    DataNavigateUrlFormatString="j avascript:fpSelProd('{0}','{1}');void(0);" 
    DataTextField="PRODID" HeaderText="產品代碼" >
    <HeaderStyle Width="80px" />
    <ItemStyle Width="80px" HorizontalAlign="Center" />
</asp:HyperLinkField>

稍微推敲了一下,這應是ASP.NET 2.0安全強化的一環。由於HyperLinkField Url中的部分內容來自於資料庫裡的欄位,而資料內容來源有可能來自使用者的輸入,無形中可能淪為駭客發動XSS攻擊的管道(如果你還不知道什麼叫XSS,可以看這篇文章),所以ASP.NET 2.0索性一看到j avascript:就把連結移除,把這管道徹底給封了。

網路上有一些解決方案: 其中有一種是改用TemplateField,自己在後端組出<a href="j avascript:....">...</a>丟出來,但依我的看法,如果沒處理好,它還是有可能提供駭客偷渡惡意Javascript Code的機會,除非對欄位內容來源及安全性有絕對的把握,否則不建議使用。

針對這個需求,我這次的做法是在TD掛上onclick事件,再透過DOM去讀出各欄位裡的產品代碼及產品名稱,雖然囉嗦了點,但比直接將資料欄位值組成Javascript來得安全,也才不辜負了ASP.NET 2.0的一番美意。

致命的吸引"綠"

我一直很鍾愛翠綠色的自然景色,也許因為習慣了台灣山林終年如一的墨綠,青翠鮮明的植物,總會特別吸引我的目光。

還清楚記得當年看到清秀佳人(Anne Of Green Gables)影集裡草地、樹葉綠成一片,心頭為之一震的感覺。即使在日常生活裡,早晨騎車上班,遇見晨光照耀在河濱公園草地灑下一地青翠;或是在辛亥基隆路口等那顆久到很欠揍的紅燈,仰頭剛好迎上從小葉欖仁嫩葉縫隙漏瀉而下的陽光,都會讓我進入片刻的放空,產生一股想翹班去草地躺著曬太陽的衝動。(幾分鐘後想起又該買奶粉了,不切實際的妄想症瞬間不藥而瘉... orz)

總之,翠綠對我始終有著致命的吸引力!

今天回草屯吃親戚的喜酒,路過一片又一片的農田,驚喜地發現正值許好多農地種起了油菜花當綠肥。盛開的鮮黃油菜花搭上翠綠的菜葉,即使色彩繽紛度遠不及歐洲風景月曆中的花卉地毯,面積比不上貴州菜花油大景的百分之一[註],但對平凡的中年宅男來說,已是上天恩賜的幸福,趕緊把之前上天給我的另兩份"幸福"也拉進鐘頭裡,咔嚓!!

[註] 我為什麼會對貴州菜花田印象深刻? 老友Ryan曾特地跑去貴州拍過一次,平凡的油菜花在"數大便是美"效應下構出不可思議的美! 不過這些照片屬其私房珍藏,各位無綠見到,但可以看看其他人的作品聞香一下。

【茶包射手專欄】坎坷的SSRS安裝經驗

SQL Server 2005 Reporting Service的設定安裝介面比之前友善多了,許多原本要下Command的動件,現在都可以用UI做掉。過去的安裝經驗都是咻咻咻就裝好,愉快的很。沒想到昨天要在兩台Windows 2000 Server上安裝,卻波折不斷...

首先,發現設定Reporting Service Configuration Manager的Web Service Identity設定,會抓不到ASP.NET Service Account而呈現空白。比對了一台過去成功安裝SSRS的Windows 2000 Server,該台機器可以抓到MachineName\ASPNET帳號。Google了一下,有人建議重跑aspnet_regiis -i、重裝IIS等有點機車的解決,因為是正式主機,當然不這麼亂一通。加上除了Web Service Identity,其他選項設定都OK,索性試了一下httq://machine/reportserver,發現網頁已有回應,但出現以下訊息:

Access to the path 'c:\Program Files\Microsoft SQL Server\MSSQL.2\Reporting Services\ReportServer\RSReportServer.config' is denied.

Process Monitor一抓,果然找到aspnet_wp.exe存取RSReportServer.config被拒的證據,給了Write權限後,ReportServer終於現身!

不過事情還沒完,兩台機器中有一台在/ReportServer OK後,/Reports管理員UI仍出現"The attempt to connect to the report server failed.  Check your connection information and that the report server is a compatible version."的訊息,Google結果,得知這會出現在使用httq://localhost/ReportServer無法連至ReportServer的情境,查詢果然該台IIS預設站台只Listen特定IP的80 Port,未包含127.0.0.1。解決方式為修改RSWebApplication.config
<ReportServerVirtualDirectory></ReportServerVirtualDirectory><--設成空值
<ReportServerUrl>httq://machineName/reportserver</ReportServerUrl>

搞定收工!

More Posts Next page »

Search

Go

<January 2008>
SunMonTueWedThuFriSat
303112345
6789101112
13141516171819
20212223242526
272829303112
3456789
 
RSS
【工商服務】


BlogLook Score and Rank

Syndication