摘要:ASP.NET 使用筆記
===========[ 快速開發 ]===========
※善用using
using完的物件會自動Close與Dispose,所以建議需要Dispose的物件都可以使用using
像是SqlConnection, SqlCommand, SqlDataReader...
1
2
3
4
|
using (SqlConnection conn = new SqlConnection( "xxx" )) { conn.Open(); } // 結束時不需要Close跟Dispose |
※資料庫交易(Transaction)不用那麼麻煩
.NET 2.0後新增了TransactionScope物件
1
2
3
4
5
|
using (TransactionScope scope = new TransactionScope()) { // TODO: SQL連線 scope.Complete(); // 交易完成 } |
如果中途因意外而跳出,無須Rollback
只要TransactionScope沒有執行Complete(),結束後就會自動Rollback
使用前必須要加入參考 System.Transactions 才能使用
※使用string.Join 省去串接功夫
假設有個陣列,你想把陣列裡的資料串成一行且用自訂符號隔開,你會怎麼做呢?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
// 一般寫法 string [] values = { "1" , "2" , "3" }; StringBuilder sb = new StringBuilder(); foreach ( string s in values) { if (sb.length() > 0) { sb.append( ',' ); } sb.append(s); } sb.ToString(); // 1,2,3 // string.Join string .Join( "," , values); // 1,2,3 // Linq寫法 values.Aggregate((i, j) => i + "," + j); // 1,2,3 |
※用out作為變數回傳值
宣告out的變數,在方法結束前一定會賦值,所以可以做為回傳值使用
1
2
3
4
5
6
7
8
9
10
11
|
private void Test() { string a, b; // 宣告時無須給初始值 GetValue( out a, out b); // 結果a="a", b="b" } private void GetValue( out string a, out string b) { a = "a" ; b = "b" ; } |
※用@來設定多行的SQL語法
雖然我個人比較推薦把SQL語法都寫在查詢產生器中(減少程式碼複雜度,還能驗證)
不過某些時候還是必須要把SQL寫在code中
從Java轉過來的人可能會這樣寫
1
2
3
|
string sql = "SELECT field1, field2" + "FROM table1" + "WHERE field3 = @Param1" ; |
其實在ASP.NET不需要這麼麻煩每一行要串接,在字串前加上@就能做到跨行
1
2
3
|
string sql = @"SELECT field1, field2 FROM table1 WHERE field3 = @Param1" ; |
※用TryParse轉換可能會出錯的字串
幾乎所有的基本型態都有TryParse方法
TryParse的特點是如果字串轉換失敗,不會拋出Exception
如果出錯後,變數會變成初始值0,如果希望同時給予不同值,可以這樣寫
1
2
3
4
5
|
int i; if (! int .TryParse( "" , out i)) { i = 100; } |
※如何要求使用者要在TextBox輸入特定格式的資料才能送出?
可以使用Validator驗證,如果格式不符,就無法Postback
(除非設定控制項屬性CausesValidation = false,使其不檢查)
另外要Validator預設會佔住畫面,如果要讓其動態顯示
需設定Validator屬性Display = Dynamic
※要清掉控制項的內容得寫好長一串,有沒有簡單的清空方法呢?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
// 一般寫法 this .TextBox1.Text = "" ; this .TextBox2.Text = "" ; this .Label1.Text = "" ; // 個人寫法: 寫個方法去呼叫 private void ClearControls( params Control[] controls) { foreach (Control control in controls) { if (control is TextBox) { (control as TextBox).Text = "" ; } else if (control is Label) { (control as Label).Text = "" ; } else if (control is HiddenField) { (control as HiddenField).Value = "" ; } } } // 使用方法 ClearControls(TextBox1, TextBox2, Label1); |
※用??代替麻煩的條件判斷
1
2
3
4
5
6
7
8
9
10
11
12
13
|
string str; if (WebConfigurationManager.AppSettings[ "xx" ] != null ) { str = WebConfigurationManager.AppSettings[ "xx" ]; } else { str = "defaultValue" ; } string str = WebConfigurationManager.AppSettings[ "xx" ] ?? "defaultValue" ; // 與上面同義 string str = val1 ?? val2 ?? val3 ?? val4; // 依序判斷val1, val2, val3, val4是否有值,有值即設定值 |
※有沒有辦法縮減SqlCommand.Parameters.AddWithValue的程式碼?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
string str = null ; SqlCommand cmd = new SqlCommand(sql){ if (str == null ) { cmd.Parameters.AddWithValue( "@Param1" , DBNull.Value); } else { cmd.Parameters.AddWithValue( "@Param1" , str); } } // 如果你寫這樣,會跳出錯誤訊息 cmd.Parameters.AddWithValue( "@Param1" , str ?? DBNull.Value); // 因為string跟object不是同一個型別 // 正確方法是 cmd.Parameters.AddWithValue( "@Param1" , ( object )str ?? DBNull.Value); |
※不會用到的控制項不要命名
有些人會很熱情的把一些顯示上的Label都取個新的ID
但這些Label其實並不會在程式上被指定到
硬要取名不但會增加選取程式碼自動完成的困難度,還可能會撞名
建議純顯示的Label就直接用預設值
※使用Google Loader載入jQuery跟jQuery UI
Google Loader是Google提供的一組API,可以讓你動態載入指定版本的jQuery JS
可以指定特定版本,也能指定最新版本。由於檔案不是放在主機上,更能減少頻寬(參考)
使用方法如下(寫在aspx中)
1
2
3
4
5
|
< script type = "text/javascript" > google.load("jquery", "1.7"); google.load("jqueryui", "1.8"); </ script > |
===========[ 功能教學 ]===========
※GridView在沒有資料的時候會變成空白,連標題列(Header)都不會顯示,要如何讓它顯示?
1. .NET 4.0新增了ShowHeaderWhenEmpty屬性
2. 自己寫物件,繼承GridView(參考)
3. 在無資料時自己丟個空資料給GridView,但是不建議,因為這樣就無法用GridView1.Rows.Count判斷是否為空(參考)
4. 在EmptyDataTemplate自行畫上Header,但更不建議,因為沒法動態修改(參考)
※GridView要如何做到按下一行自動選取該行(select row)
內建竟然沒這功能,有點誇張
在GridView的RowDataBound輸入以下code
1
2
3
4
5
6
7
8
|
protected void GridView1_RowDataBound( object sender, GridViewRowEventArgs e) { if (e.Row.RowType == DataControlRowType.DataRow) { e.Row.Attributes[ "onclick" ] = ClientScript.GetPostBackClientHyperlink( this .GridView1, "Select$" + e.Row.RowIndex); e.Row.Style.Add(HtmlTextWriterStyle.Cursor, "pointer" ); // 滑鼠移上去時,變成點選樣式 } } |
但輸入完後會出現以下訊息
因為這個方法的PostBack資料不是原始的控制項發出來的,會有安全性上的問題
解決法
1. 照著說明,設定EnableEventValidation = false
不過這樣會有被跨站攻擊的風險,不建議(參考)
2. 覆寫該頁Render,註冊其事件驗證,推薦
1
2
3
4
5
6
7
8
|
protected override void Render(HtmlTextWriter writer) { for ( int i = 0; i < this .GridView1.Rows.Count; i++) { Page.ClientScript.RegisterForEventValidation( this .GridView1.UniqueID, "Select$" + i.ToString()); } base .Render(writer); } |
※在刪除按鈕點下去前先跳出一個確認視窗,選擇確定才PostBack
Button的OnClientClick輸入
return confirm("Are you sure?");
※使用ASP.NET的按鈕控制項,使其按下去能呼叫JavaScript,但是不要PostBack
Button的OnClientClick是可以輸入多句JS語法的
只要最後加上return false;就能避免PostBack
javascript:xxx(); return false;
※如何顯示MessageBox
ASP.NET沒這東西,你只能動態註冊JS呼叫alert
1
|
ScriptManager.RegisterStartupScript( this , this .GetType(), "alert" , "alert('" + Message + "');" , true ); |
※如何叫出ConfirmBox,按鈕是寫Yes/No/Cancel而不是一般的OK/Cancel?
不但ASP.NET沒這東西,連JS都沒有
你可以用jQuery+jQuery UI的Modal confirmation,自己創立一個Dialog
※JS的Confirm視窗可不可以做到按下是或否跳到不同PostBack的效果
可以,但要用JS去呼叫PostBack方法
1
2
3
4
5
6
7
8
9
|
function check() { var ret = confirm( 'Are you sure?' ); if (ret) { __doPostBack( 'question' , 'true' ); } else { __doPostBack( 'question' , 'false' ); } } |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
protected void Page_Load( object sender, EventArgs e) { string event_target = Request.Form.Get( "__EVENTTARGET" ); string event_argu = Request.Form.Get( "__EVENTARGUMENT" ); if (event_target == "question" ) { if (event_argu == "true" ) { // TODO: true } else if (event_argu == "false" ) { // TODO: false } } } |
※要怎麼做到按下按鈕後跳出新視窗(open new window),在新視窗輸入內容(Ex.帳號密碼)後回傳(return value)的效果
純ASP.NET無法做到,只能使用JS,請搜尋關鍵字showModalDialog
以下方法不是最好的方法...如果有更好的作法請跟我說
假設要從A頁面呼叫B頁面,輸入完資料再回傳A頁面
在A.aspx寫入此JS (要直接寫或用動態註冊皆可)
1
2
3
4
|
var returnValue = window.showModalDialog(url, "" , "dialogWidth:300px;dialogHeight:300px;" ); // 還有很多參數可用 if (returnValue != undefined) { __doPostBack(target, returnValue); // target自訂 } |
在B.aspx寫入此JS
1
2
3
4
|
function selectBack(valueString) { window.returnValue = valueString; window.close(); } |
在B.aspx的控制項上呼叫此方法
javascript:selectBack('" + encodeKeyString + "')
最後在A.aspx.cs接收此PostBack參數
※網頁原始檔部份有內建的排版(format)工具嗎?
網頁全選後按右鍵,選擇"格式化選取範圍"
※要怎麼建立全域變數?
不可以用以下寫法
private static int i;
因為其他人打開網頁時所有人看到的變數是相同的
比方說有個網頁有個按鈕,點一下會執行i++,畫面上會顯示i的內容
第一個人進來時看到i=0,點了五次,i=5
第二個人進來時會看到i=5而非i=0
比較好的方法是使用HiddenField(但只能儲存string)
或用ViewState["key"]的方式去儲存/取得內容(可以儲存所有格式)
如果嫌太麻煩可以這樣使用
1
2
3
4
5
|
private int value_name { get { return ( int )ViewState[ "key" ]; } set { ViewState.Add( "key" , value); } } |
但是在Page_Load的時候就必須給他初始值,不然在沒有初始值的時候取值會出錯
※如何避免SQL衝突 (兩人修改同一筆資料)
假設有個資料表是這樣(id是Primary Key)
id name
1 小明
2 小華
如果資料庫要修改欄位內容,一般SQL會寫
Update name = @Name Where id = @id
但如果同時有兩個人要修改小明的name,就會變成"後進先寫入"
也就是後面寫入的資料會蓋過前面寫入的資料
解決法是把SQL改寫成這樣
Update name = @Name Where id = @id and name = @origin_name
這樣當第一個人把"小明"修改成"小明1"之後
第二個人就無法找到name=小明的資料,所以不會覆蓋到第一個人的修改結果
※DropDownList要怎麼根據Text或Value改變他的選取值
1
2
|
DropDownList1.SelectedIndex = DropDownList1.Items.IndexOf(DropDownList1.Items.FindByText( "text" )); DropDownList1.SelectedValue = "XXX" ; // 如果只要根據Value選擇,直接這樣寫就好 |
要怎麼使用Ajax技術?
1. 使用Ajax Control Toolkit,這是一個用純JavaScript寫成的Ajax技術,都是控制項形式
2. 使用jQuery / jQuery UI
3. 使用Juice UI,這是一個用jQuery寫成的Ajax技術,也是控制項形式,但功能上比jQuery UI少
(參考)
※如何把控制項自動產生的編號歸零?
每拉一個控制項,會在後面自動增加號碼,例如Label1, Label2
其實那個號碼是去找最大控制項的號碼+1
比方說目前網頁有控制項Label1, Label99
下一次拉出的控制項ID就會自動命名為Label100
要怎麼讓他歸零?只要把前面號碼的都刪掉就好了
上面例子中,只要把Label99刪掉,下次自動命名就是Label2
※產生本機資源後後悔了,要怎麼移除掉該頁控制項的meta:resourcekey=xxx
尋找目標 meta\:resourcekey=[^:b>]*"
取代成 (不輸入)
並使用規則運算式即可
※DateTime要怎麼重新設定時間,且不使用new
沒有辦法,請直接用new給予新值
1
2
|
DateTime dt = new DateTime(2012, 12, 12); dt = new DateTime(2011, 11, 11); |
DateTime是struct,所以直接取代掉就好了
struct是value type,所以並不會浪費記憶體
※如何讓DateTime指定為該月最後一天?
1. DateTime.DaysInMonth(year, month);
2. new DateTime(year, month, 1).AddMonths(1).AddDays(-1);
※如何跳出一個月曆讓使用者選取日期?
1. 用Ajax Control Toolkit的CalendarExtender
2. 用jQuery UI的Datepicker
3. 用專門顯示月曆的套件,如JSCal2(還能選時間)
※要怎麼用SqlDataReader讀取SqlDataSource的Select?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
// 將SqlDataSource的Select轉成SqlDataReader物件的方法 // 特別注意: 必須先將SqlDataSource的DataSourceMode屬性設定為"DataReader",否則轉型會失敗 using (SqlDataReader reader = SqlDataSource1.Select(DataSourceSelectArguments.Empty) as SqlDataReader) { while (reader != null && reader.Read()) { reader[ "xxx" ].ToString(); } } // 將SqlDataSource的Select轉成DataTable物件的方法 // 特別注意: SqlDataSource的DataSourceMode屬性必須為"DataSet" (這是預設值) DataTable dt = (SqlDataSource1.Select(DataSourceSelectArguments.Empty) as DataView).ToTable(); // 順便一提,SqlDataReader所占用的資源比起DataTable小很多 // 若沒有要對資料表作處理,可盡量使用SqlDataReader |
※SQL要怎麼把很多筆資料(multi row)變成單行字串(single string)?
比方說今天有資料如下
ID Name
1 Name1
1 Name2
2 Name1
2 Name3
2 Name5
希望轉成以下的欄位
ID Name
1 Name1,Name2
2 Name1,Name3,Name5
如果是MSSQL有個偷吃步的方法,就是使用FOR XML PATH('')語法
將取得的欄位轉變成一個用空白tag包覆的XML內容
SELECT A.ID,
(
SELECT B.Name + ','
FROM [TableB] AS B
WHERE B.ID = A.ID
FOR XML PATH('')
) AS Name
FROM [TableA] As A
===========[ 錯誤修正 ]===========
※DropDownList當項目有相同的Value的時候,點選的結果不正確
這個問題是PostBack的原罪,相同Value時回傳給Server的值是相同的(參考)
1. 自己寫DropDownList控制項解決
2. 在綁定後自己修改item value,使其不重複
※使用自訂元件時出現「無法在屬性xxx 設定xxx」
這是Visual Studio 2008的固有Bug,解決法:請下載更新檔
(更新檔)(參考)
※使用動態註冊JS之後,某些TextBox輸入的文字會消失
不要用RegisterClientScriptBlock跟RegisterClientScriptInclude
使用RegisterStartupScript,他會把JS放到網頁最下面,確保前面的JS會被執行到(參考)
※GridView某欄位在設定Visible=false後,PostBack之後無法取得該欄位的值
因為這個欄位在一開始產生網頁的時候就不會寫進去,所以自然無法取得不存在的資料
解決法
1. HeaderStyle跟ItemStyle設定CSS,CSS內容為display:none;
這樣就會Bind資料,且在畫面上看不到該欄位
2. 在RowDataBound的時候指定該欄位Visible = false;
但有壞處就是如果你的GridView沒有資料且沒資料時可以顯示標題欄
因為沒資料時不會呼叫RowDataBound,所以該欄標題會顯示
因此建議還是使用第一種方法
1
2
3
4
5
6
7
|
protected void GridView1_RowDataBound( object sender, GridViewRowEventArgs e) { if (e.Row.RowType == DataControlRowType.Header || e.Row.RowType == DataControlRowType.DataRow) { e.Row.Cells[0].Visible = false ; } } |
※設定GridView的SelectIndex後,不會觸發SelectedIndexChanged事件?
是的,每次寫了更換SelectIndex後你要自行去呼叫SelectedIndexChanged
.NET 4.0很好心的加了SelectRow方法,可以設定Index後自動呼叫SelectedIndexChanged事件
※IE 9的CSS Selector跟其他瀏覽器不同,導致圓角矩形顯示上有問題
這情況我建議強迫IE 9使用IE 8的網頁排版方式,這樣圓角矩形的問題就不用麻煩了
方法有兩種,一種是直接在aspx中<title>的下方加上
<meta http-equiv="X-UA-Compatible" content="IE=8" />
或是在aspx.cs動態加上這段
1
2
3
4
|
protected void Page_Init( object sender, EventArgs e) { Context.Response.AddHeader( "X-UA-Compatible" , "IE=8" ); } |
===========[ 觀念解析 ]===========
※建議用WebConfigurationManager取代ConfigurationManager
(參考)
一般書上都用ConfigurationManager取得Web.config的設定值
其實WebConfigurationManager才是正統的作法
雖然兩者目前是通用的,不過用正統方法還是比較保險一點
1
2
|
WebConfigurationManager.ConnectionStrings[ "xx" ].ConnectionString; // 標準寫法 WebConfigurationManager.AppSettings[ "xx" ]; // 標準寫法 |
※字串直接串接比較好還是用string.Format串接比較好?
用string.Format串接比較快,因為其內部是使用StringBuilder實作
※用string.Empty好還是""好?
.NET 2.0之前,""會產生一個新物件,.NET 2.0後寫哪個都一樣
個人建議使用"",因為程式碼越短越好
(參考1)(參考2)
※string跟String有什麼不同?
完全一樣,string是String的別名
但如果是要宣告變數,個人建議用string比較統一(參考)
※decimal跟double的差別?
Decimal 不是浮點數資料型別。Decimal 結構會保存二進位整數值,加上正負號位元和整數縮放比例,指定值的哪一部分是小數部分。
簡單的說,double因為是用byte去儲存小數,所以每次都會有誤差,而decimal是用十進位去存小數,所以不會有誤差
如果要儲存金額等這種不允許誤差的數值,就要用decimal(參考)
※int, double是class嗎?
不是,其實是struct,可能是因為處理速度上比較快的關係
※所以string也是struct嗎?
不是,string是class,可以當object來用,不像struct傳遞一次就要copy一次
另外就是string pool的問題,用struct應該會有困難
※那為什麼List可以直接放基本型別?
1
|
List< int > list = new List< int >(); // 像這樣 |
因為C#會自動幫你做轉換(參考)
※as跟強制轉型有什麼差別?
如果不能轉型的話,as會回傳null,強制轉型會丟出Exception
※Label跟Literal有什麼差別?
Label在編譯成網頁時會用span包起來,Literal則會直接轉成網頁文字
另外Label可以設定CSS style,Literal則否
如果在純顯示文字的情況,建議直接用Literal,這樣能用CSS直接調整整個頁面的字型
※int.Parse跟Convert.ToInt32有什麼差別?
1. int.Parse只能傳入string,Convert.ToInt32可以讀取各種型態
2. 當傳入參數為null時,int.Parse會拋出exception,Convert.ToInt32會回傳0
(參考)
※if跟switch哪個效率較好?
判斷int的情況,switch完勝,就算在第一個條件就符合的情況下,還是switch較快
這是因為switch會建立binary decision tree,所有的case都耗時O(1)
所以適合用switch的情況就盡量不要用if
當然if能用複合條件判斷,在使用上比較靈活
以下為測試結果
if(1) = 00:00:00.0320018
if(2) = 00:00:00.0430024
if(3) = 00:00:00.0470027
if(4) = 00:00:00.0600034
if(5) = 00:00:00.0630036
if(6) = 00:00:00.0730041
if(7) = 00:00:00.0820047
if(8) = 00:00:00.0880050
switch(1) = 00:00:00.0230013
switch(2) = 00:00:00.0230013
switch(3) = 00:00:00.0230013
switch(4) = 00:00:00.0250014
switch(5) = 00:00:00.0240014
switch(6) = 00:00:00.0250014
switch(7) = 00:00:00.0240014
switch(8) = 00:00:00.0240014
不過判斷string的情況,若前面條件就符合的話,if會比較快
以下為測試結果
if(1) = 00:00:00.1010058
if(2) = 00:00:00.1510086
if(3) = 00:00:00.2160124
if(4) = 00:00:00.2850163
if(5) = 00:00:00.3520201
if(6) = 00:00:00.4160238
if(7) = 00:00:00.4860278
if(8) = 00:00:00.5600321
switch(1) = 00:00:00.3790217
switch(2) = 00:00:00.3810218
switch(3) = 00:00:00.4210241
switch(4) = 00:00:00.3800217
switch(5) = 00:00:00.3820219
switch(6) = 00:00:00.3900223
switch(7) = 00:00:00.3850220
switch(8) = 00:00:00.3800217
===========[ 工具連結 ]===========
※修正舊版IE不能顯示Png透明度的問題
IEPngFix
※JS線上排版工具
Online JavaScript beautifier
※CSS漸層產生器
Ultimate CSS Gradient Generator
跟一般產生器不同的是,他的語法能支援到IE 6~9
會一併產生filter: progid:DXImageTransform.Microsoft.gradient
※CSS3按鈕產生器
CSS3 Button Generator
除了基本產生按鈕的功能外,還能使用別人分享調整好的按鈕樣式