2009年9月29日 星期二

在 ASP.Net 程式裡避免 User 連續觸發 Button

當網路回應速度不快時,user 壓下某個 button 後,畫面卻還停留著而尚未回應,此時如果再壓一次該 button ,則就會再觸發一次該 button 的事件。為了避免這問題,可以透過一段 javascript 來處理。

btn.Attributes["onclick"] = "this.style.display='none'; alert('some message..');";

如果原先這個 button 已經有呼叫 confirm( ) 時,例如會詢問 user 是否確定要執行,如果「」,則將 button 隱藏是可達成避免連續觸發的問題,但如果「」,實際上是不需要隱藏 button 的。所以,如果是已經有 confirm( ) 時,則先用一個變數來存 confirm( ) 的值,再依據這個值來決定是否要隱藏 button。

btn.Attributes["onclick"] = "var vRW=confirm('是否確定要重新修改?'); if(vRW) { this.style.display='none'; return true; } else { return false; } ";

2009年9月27日 星期日

項目'LocalSqlServer'已加入

在 web 站台上,突然出現 項目'LocalSqlServer'已加入 的錯誤訊息,並且指出是 web.config 發生的。而檢查目前的 web.config 檔,有這下面一段:

< add name="LocalSqlServer" connectionString="Data Source=192.168.1.1,5433;Initial Catalog=Test;User ID=sa;Password=sa" providerName="System.Data.SqlClient"/>


自己也只有這個地方在設定資料庫連線而已,真不知道為何還是會出現已加入的錯誤訊息,但把程式安裝在其他台電腦卻又是正常的。所幸,在MSDN上發現一篇類似問題的解法,就是在 < add name="LocalSqlServer" /> 之前,先強制移除,也就是使用 < remove
name="LocalSqlServer"/> 。這招真的有效,下次如果有人也遇到相同的狀況,可以試試。

BinarySearch (二)

在先前 BinarySearch(一)的介紹,陣列裡面的元素為字串,但陣列所支援的,並非只有字串而已,而現在正要討論的,是當陣列內容是我們自己定義的類別時,還可以用 BinarySearch 嗎?

首先,我定義了一個簡單的 Person 類別:


class Person
{
public string Name = string.Empty;
public string Department = string.Empty;

public Person (string strName, string strDep)
{
Name=strName;
Department=strDep;
}

public string ShowDetail()
{
return string.Format("Name:{0}, Dep:{1}", Name, Department);
}
}





接著,我很開心的一一鍵入我的測試資料:

Person p1 = new Person("paladin", "BPO");
Person p2 = new Person("hugo", "BPO");
Person p3 = new Person("ken", "BPO");
Person p4 = new Person("keen", "BPO");
Person p5 = new Person("polly", "BPO");
Person p6 = new Person("panda", "BPO");
Person p7 = new Person("lillian", "BPO");
Person p8 = new Person("gemma", "BPO");
Person p9 = new Person("hana", "BPO");

Person[] arrayOffice = { p1, p2, p3, p4, p5, p6, p7, p8, p9 };
Array.Sort(arrayOffice);




當我寫到這裡時,傻住了,目前我的 arrayOffice 陣列,可以排序ㄇ?當程式一執行,馬上就丟出了不好的消息:「無法比較陣列中的兩個元素」。的確,字串、數字是可以用來比較大小,但是自己所定義的類別,如果你沒定下比較規則,那真的是拿芭樂比蓮霧了。

所以,重新修改了自己所定義的 Person 類別,讓他實作 IComparable 介面 來解決。


class Person :IComparable //宣告使用了 IComparable 介面
{
// 原先的程式碼
// XXXXXXXXXXXXX
// XXXXXXXXXXXXX

//實做 IComparable 介面
int IComparable.CompareTo(object obj)
{
Person p2 = obj as Person;
if (p2 == null) throw new ArgumentException("Object is not a Person!");
int iNameResult = this.Name.CompareTo(p2.Name);
if (iNameResult == 0)
{
int iDepResult = this.Department.CompareTo(p2.Department);
return iDepResult;
}
else
{
return iNameResult;
}
}



在自己所實作的 CompareTo( )裡,先比較兩個 Person 的 Name 是否一致,如果一致,接著再比較Department 是否一致。也就是當 Name 與 Department 相同時,才視這兩個 Person 物件為相等。



當解決了 Sort( ) 的問題後,接下來的 BinarySearch( )就不是問題了,於是可以繼續將先前打住的程式繼續往下寫下去。



Person p1 = new Person("paladin", "BPO");
Person p2 = new Person("hugo", "BPO");
Person p3 = new Person("ken", "BPO");
Person p4 = new Person("keen", "BPO");
Person p5 = new Person("polly", "BPO");
Person p6 = new Person("panda", "BPO");
Person p7 = new Person("lillian", "BPO");
Person p8 = new Person("gemma", "BPO");
Person p9 = new Person("hana", "BPO");

Person[] arrayOffice = { p1, p2, p3, p4, p5, p6, p7, p8, p9 };
Array.Sort(arrayOffice);
foreach (Person p in arrayOffice)
Console.WriteLine(p.ShowDetail());

Person pSearch = new Person("panda", "BPO");
int iIndex = Array.BinarySearch(arrayOffice,pSearch);
Console.WriteLine("Index of 'panda' object is :" + iIndex.ToString());

Console.Read();

BinarySearch (一)



在 .Net 的 Array 物件,有個 BinarySearch 方法,光從字面上猜,應該是用來「找查」用的。 於是寫了一段 code 來試試。




string strInput = "paladin,hugo,ken,keen,polly,panda,lillian,gemma,hana";
string[] strArray = strInput.Split(',');

foreach (string s in strArray)
{
Console.WriteLine(s);
}

int iLocation=Array.BinarySearch(strArray, "panda");
Console.WriteLine("Index of 'panda' is :"+iLocation.ToString());
Console.Read();



蠻不給面子的,竟然沒用!
後來發現,要使用 BinarySearch( ),必須要先將資料進行排序才行,也就是說,不排序,就不給用。而在 Array 物件,他也同時提供了 Sort( )方法,正是使用 BinarySearch( ) 所需要的。於是修改了一下上面的程式,加上 Array.Sort(strArray) 。




string strInput = "paladin,hugo,ken,keen,polly,panda,lillian,gemma,hana";
string[] strArray = strInput.Split(',');
Array.Sort(strArray);

foreach (string s in strArray)
{
Console.WriteLine(s);
}

int iLocation=Array.BinarySearch(strArray, "panda");
Console.WriteLine("Index of 'panda' is :"+iLocation.ToString());
Console.Read();




的確,加上排序後,查詢的功能就正確了。但這時心裡頭不免偷偷地「暗」一下微軟,幹麼不乾脆一點,還要讓人這麼麻煩。後來發現,其實這是有原因的。Binary Search (二分搜尋法),是用來搜尋已經排序過的資料。基本上,是先將要比較的資料與所有資料的中間值作比較,根據比較結果的大、小或相等,再來決定下一個搜尋範圍或直接得到搜尋結果。而這種搜尋方式,當然會比我們寫一個迴圈從第一筆慢慢找到最後一筆還來的快許多。

然而,別忘了使用 BinarySearch ( ) 的前提是必須先將資料排序,所以必須將排序所花的時間與搜尋時間加在一起才公平。在效能上來說,如果你要查詢的陣列只有使用一次而已,循序搜尋也不見得會比較慢,因為當資料量很龐大時,光排序也需要花不少時間。相反地,如果你的陣列會一直不斷的被重複使用,只要事先執行一次排序,之後就只需不斷地呼叫 BinarySearch( )則會較佔優勢,這正也是為何自己先前質疑使用 BinarySearch( ) 為何要與 Sort( ) 分開的原因。

2009年9月26日 星期六

Tomcat 6 & JDK 6

紀錄目前在 Windows XP 環境下設定 JSP 的開發環境,選擇的工具為 Tomcat 6 與 Java 的JDK 6 版本。這次所使用的軟體,可以到以下連結去下載:

JAVA JDK 6 [下載]
Apache Tomcat 6.0.20 [下載]

JAVA 所選擇的是 Java SE Development Kit 的 JDK 6 Update 16 版本。

Tomcat 所選擇的是 Binary Distributions 的 zip 格式。

首先,需要先安裝 JAVA 的環境。解開並執行剛剛所下載的 JDK 即可。

接著,安裝 Tomcat。執行所下載的 .EXE 執行檔,我在安裝路徑上選擇了 C:\Tomcat。在安裝動作的尾聲,會跳出一個視窗,問你目前要用來呼叫 JAVA 的路徑,則是填寫上一個安裝JAVA步驟後的位置:
C:\Program Files\Java\jdk1.6.0_16

緊接著,他會跳出一個畫面,提供你設定 Apache Tomcat 的管理員帳號及密碼。這管理員資訊在安裝完後,是存放在 C:\Tomcat\conf\tomcat-users.xml 。

當安裝完 JAVA 與 Tomcat 後,需要作一些環境設定。
打開 [我的電腦] - [右鍵] - [內容] - [進階] - [環境變數] - 在環境變數下選擇 [新增]

變數名稱變數值
CLASSPATHC:\Program Files\Java\jdk1.6.0_16\bin
CATALINA_HOMEC:\Tomcat
JAVA_HOME C:\Program Files\Java\jdk1.6.0_16


如此,就完成了 Windows XP 安裝 JSP 開發環境的安裝了。

在 Tomcat 過去的版本,都會在根目錄下有以下幾個主要資料夾產生:





資料夾說明
bin程式指令(啟動與停止)
common執行所需jar函式庫
conf 設定檔
logs日誌檔
webapps 存放網頁應用程式目錄


但在 6.X 版,已經廢棄了 common 資料夾了。而原先在 /common/lib ,現在則是
將 /lib 資料夾提到根目錄下。所以在根目錄下面,就可以找到 lib 資料夾了。原先用來
存放擴展的 jar 檔案,也因此改放到根目錄下的 /lib 資料夾下(也就是說,當你有需要
引用別人包好的 jar 函式類別時,記得要放到 /lib 下方)。

參考網址一:http://tw.myblog.yahoo.com/aloha-tw/article?mid=359
參考網址二:http://www.javayou.com/diary/6413

2009年9月21日 星期一

An INSERT EXEC statement cannot be ne...


最近在撰寫一支 SQL Server Store Procedure 時,一直發生
An INSERT EXEC statement cannot be nested 的錯誤訊息。
SQL 語法如下:
create table #tFinishAll ( IsFinish int )
insert into #tFinishAll (IsFinish) 
exec pr_prog_section3_is_report_finish  
當我單純的只有執行 exec pr_prog_section3_is_report_finish 時,
結果是正常的,但跟 insert into #tFinishAll (IsFinish)  一起執行時,
就會出錯。

詳細檢視了 pr_prog_section3_is_report_finish 這支 Store Procedure,
發現它裡面也有類似先建立一個暫存資料表,然後又透過 執行 
Store Procedure 來新增資料的方式。

原來,這種情況在 SQL 2005 就會被視為 Nested Insert Exec而被禁止。
詳細文章可參考:http://www.windows-tech.info/15/fe648af19f711aba.php
所以,我就需要改寫自己的 Store Procedure ,改成透過 OPENROWSET
的方式來解決。於是將自己的程式改寫如下:

SELECT *
FROM OPENROWSET
(
'SQLOLEDB',
'Server=SQLServer;uid=test_id;pwd=test_pwd',
' SET FMTONLY OFF exec pr_prog_section3_is_report_finish '
)


除了使用 OPENROWSET 語法改寫之外,另外要注意的,就是要加上
SET FMTONLY OFF關鍵字,宣告這個 Script 語法裡面是可以允許建立
暫存資料表的。

使用 C# 的 "as" 關鍵字

使用 C# 的 "as"  關鍵字

 

自己在C#語言關於使用轉型(cast)的記憶中,常常都很直覺的就會使用
強迫轉型方式來處理。就以 Person 的類別來舉例:

 


    class Person
    {
        public string Name = string.Empty;
        public string ID = string.Empty;


        public Person(string _name,string _id)
        {
            Name = _name;
            ID = _id;
        }


        public override bool Equals(object obj)
        {
            Person p = (Person)obj;
            return p.Name == this.Name && p.ID == this.ID;
        }



    }


 

Person p =(Person) obj;
會直接就用 (Person)去強迫轉型,原因是因為自己認為 obj 一定是屬於
Person型別,才會有如此的寫法。但是,真的能保證 obj 一定是屬於
Person型別嗎?這可能是自己的一廂情願吧!

當 obj 並非屬於 Person 型別時,系統就會丟出 InvalidCastException
的錯誤訊息。

為了避免轉型錯誤的發生,可以使用 "as" 這關鍵字。他會先去幫你作轉型
的動作,如果成功,自然就會回傳轉型成功後的物件型別;如果失敗,則會
回傳 null。所以,當使用了 "as" 關鍵字後,在透過判斷回傳值是否為 null
來完成型別轉換工作。

而上述的程式,修正如下:

 

        public override bool Equals(object obj)
        {
            Person p = obj as Person;

            if (p==null) return false;

            return p.Name == this.Name && p.ID == this.ID;
        }

 


 

 

2009年9月20日 星期日

Overriding Equals

Overriding Equals

 

當我們定義好一個類別之後,如果用這個類別來創建2個實體(Instance),
這兩個實體在記憶體分別儲存存在不同的位址,由此看來,這兩個實體的確
是不同的。 

以下面 Person 類別來說明: 


    class Person

    {

        public string Name = string.Empty;

        public string ID = string.Empty;

        public Person(string _name,string _id)

        {

            Name = _name;

            ID = _id;

        }

    }


 


Person 有兩個公開屬性,分別是 Name 與 ID。
如果程式如下:
Person p1=New Person("paladin","01");
Person p2=New Person("paladin","01"); 


這 p1 與 p2 ,雖然所傳入的值,都是 Name="paladin" ,ID="01",
但他們彼此卻是不相同的,可以透過以下的 Equals( )來知道。 


p1.Equals(p2)  


他會回傳 False。 


然而,今天如果我們想要重新定義,只要是 Name 與 ID 相同,就要視
兩個實體為相同,可以嗎? 


可以的,此時,可以透過覆寫 Equals( ) 來達成。


 


在 Person 類別裡,針對這個類別的 Equals( ) 方法進行覆寫,只要
Name 與 ID 相同,就視為相同的物件。 


將 覆寫的程式描述如下:


 


 


        public override bool Equals(object obj)
        {
            Person p = (Person)obj;


            return p.Name == this.Name && p.ID == this.ID;
        }


 


如此,執行 p1.Equals(p2) 時,
則會去比較 p2.Name 是否等於 p1.Name 且 p2.ID 是否等於 p1.ID ,
然後以此來回傳 Equals( ) 的值。


 


完整的 Person 的定義如下:


 


    class Person
    {
        public string Name = string.Empty;
        public string ID = string.Empty;


        public Person(string _name,string _id)
        {
            Name = _name;
            ID = _id;
        }


        public override bool Equals(object obj)
        {
            Person p = (Person)obj;


            return p.Name == this.Name && p.ID == this.ID;
        }



    }


 


 


而在實際比較時,就可以發現,此時 p1.Equals(p2) 就會回傳 True 了


 


        static void Main(string[] args)
        {
            Person p1 = new Person("paladin", "01");
            Person p2 = new Person("paladin", "01");


            Console.WriteLine(string.Format("p1 is the same with p2 ? {0}", p1.Equals(p2) ? "True" : "False"));
            Console.ReadKey();
        }


 


 



2009年9月11日 星期五

hammer & nail

今天在看書時,偶然看到這句英文:「If all you have is a hammer, everything looks like a nail.」。細細玩味後,覺得蠻有意思的。照字面上直譯,是說:當你有了一隻槌子之後,任何東西對你來說就像是釘子一樣不足為懼。

在 IT 界也是常常看到這種現象。偶而出現一種程式語言、或一項產品,有些人就會冀望它能夠實現任何東西,就會期待他能有十八般武藝什麼功能都能做得到。若是如此,就不免落得「以井觀天」,不切實際了。

2009年9月2日 星期三

PuTTY 連 Ubuntu 中文出現亂碼

PuTTY 官方網站:PuTTY: A Free Telnet/SSH Client

中文亂碼解決辦法:

開啟 PuTTY -->左側選單 Window-->Translation-->Received data assumed to be in which character set-->改為『UTF-8』

最後左側選單的 Session-->Saved Sessions-->輸入連線資訊,
例:17-0526xxx-01-->按 Save 儲存

如此就不需要每次連線都去修改。