http://blog.csdn.net/jabby12/archive/2004/08/04/64027.aspxADO.NET提供了Connection來(lái)連接數(shù)據(jù)庫(kù),同時(shí)也提供了Command對(duì)象來(lái)查詢數(shù)據(jù)庫(kù)。同Connection對(duì)象一樣,Command也有兩種:OleDbCommand和SqlCommand.其區(qū)別同Connection對(duì)象。
要操縱數(shù)據(jù)庫(kù),必須先使用Connection來(lái)連接到數(shù)據(jù)庫(kù),再創(chuàng)建一個(gè)Command來(lái)查詢。有幾種創(chuàng)建方式,例:
SqlCommand cmd;
string strCon="server=localhost;database=Northwind;Trusted_Connection=Yes;";
string strqry="select * from Categories";
SqlConnection con=new SqlConnection(strCon);
con.Open();
1cmd=con.CreateCommand(); //這里使用用Connection對(duì)象的CreateCommand方法來(lái)創(chuàng)建一個(gè)Command對(duì)象。
cmd.CommandText=strqry;
//SqlDataReader reader=cmd.ExecuteReader();
?2cmd=new SqlCommand();?? //直接使用new 關(guān)鍵字來(lái)創(chuàng)建
cmd.CommandText=strqry;
?cmd.Connection=con;?? //設(shè)置與數(shù)據(jù)庫(kù)的連接
3cmd=new SqlCommand(strqry,con); //直接在new的時(shí)候帶兩個(gè)參數(shù)來(lái)創(chuàng)建
執(zhí)行方式:
(主要有這么幾種,cmd.ExecuteReader();cmd.ExecuteNonQuery();cmd.ExecuteScalar();cmd.ExecuteXmlReader();)
1,ExecuteReader();返回一個(gè)SqlDataReader對(duì)象或OleDbDataReader對(duì)象,這個(gè)看你的程序的需要去 做。可以通過(guò)這個(gè)對(duì)象來(lái)檢查查詢結(jié)果,它提供了“游水”式的執(zhí)行方式,即從結(jié)果中讀取一行之后,移動(dòng)到另一行,則前一行就無(wú)法再用。有一點(diǎn)要注意的是執(zhí)行之后,要等到手動(dòng)去調(diào)用Read()方法之后,DataReader對(duì)象才會(huì)移動(dòng)到結(jié)果集的第一行,同時(shí)此方法也返回一個(gè)Bool值,表明下一行是否可用,返回True則可用,返回False則到達(dá)結(jié)果集末尾。
使用DataReader可以提高執(zhí)行效率,有兩種方式可以提高代碼的性能:一種是基于序號(hào)的查找,一個(gè)是使用適當(dāng)?shù)腉et方法來(lái)查找。因?yàn)椴樵兂鰜?lái)的結(jié)果一般都不會(huì)改變,除非再次改動(dòng)查詢語(yǔ)句,因此可以通過(guò)定位列的位置來(lái)查找記錄。用這種方法有一個(gè)問(wèn)題,就是可能知道一列的名稱而不知道其所在的位置,這個(gè)問(wèn)題的解決方案是通過(guò)調(diào)用DataReader 對(duì)象的GetOrdinal()方法,此方法接收一個(gè)列名并返回此列名所在的列號(hào)。例:
int id=reader.GetOrdinal("CategoryName");
while(reader.Read())
{
Response.Write(reader[id]);
reader.Close();
?至于第二種方式很直觀,例:
while(reader.Read())
{
?Response.Write(reader.GetInt32(0).ToString()+" "+reader.GetString(1).ToString()+"
");
}
DataReader的GetInt32()和GetString()通過(guò)接收一個(gè)列號(hào)來(lái)返回一個(gè)列的值,這兩種是最常用的,其中 還有很多其它的類型。
(注:DataReader對(duì)象在調(diào)用Close()方法即關(guān)閉與數(shù)據(jù)庫(kù)的連接,如果在沒(méi)有關(guān)閉之前又重新打開(kāi)第二個(gè)連接,則會(huì)產(chǎn)生一條異常信息)
2.,ExecuteNonQuery()?這個(gè)方法并不返回一個(gè)DataReader對(duì)象,而是返回一個(gè)int類型的值,即在執(zhí)行之后在數(shù)據(jù)庫(kù)中所影響的行數(shù)。
例:
int affectrows=cmd.ExecuteNonQuery();
Response.Write(affectrows +" 條記錄受影響");
?3,ExecuteScalar() 這個(gè)方法不接受任何參數(shù),僅僅返回查詢結(jié)果集中的第一行第一列,而忽略了其它的行和列,而且返回的是一個(gè)object類型,在使用之前必須先將它強(qiáng)制轉(zhuǎn)換為所需類型。如果返回的僅僅是一個(gè)單獨(dú)的數(shù)據(jù)元,則可以使用此方法來(lái)提高代碼的性能。例:
string strCon="server=localhost;database=Northwind;Trusted_Connection=Yes;";
string strqry="select count(*) from Categories";
SqlConnection con=new SqlConnection(strCon);
con.Open();
SqlCommand cmd=con.CreateCommand();
int i=Convert.ToInt32(cmd.ExecuteScalar()); //必須強(qiáng)制轉(zhuǎn)換
4,ExecuteXmlReader() 此方法用于XML操作,返回一個(gè)XmlReader對(duì)象,由于系統(tǒng)默認(rèn)沒(méi)有引用 System.Xml名空間,因此在使用前必須前引入。例:
string strCon="server=localhost;database=Northwind;Trusted_Connection=Yes;";
SqlConnection con=new SqlConnection(strCon);
con.Open();
SqlCommand cmd = new SqlCommand("select * from Categories FOR XML AUTO, XMLDATA", con);
XmlReader xr=cmd.ExecuteXmlReader();
Response.Write(xr.AttributeCount); //這里獲取當(dāng)前節(jié)點(diǎn)上的屬性個(gè)數(shù)?
xr.Close();
執(zhí)行完畢之后,照樣要顯式地調(diào)用Close()方法,否則會(huì)拋出異常。
使用參數(shù)化的查詢
先看一段SQL語(yǔ)句:select CategoryID,Description from Categories where CategoryID=? 其中的問(wèn)號(hào)就是一個(gè)參數(shù)。但在使用的時(shí)候必須是帶有@前綴的命名參數(shù),因?yàn)?NET數(shù)據(jù)提供程序不支持這個(gè)通用的參數(shù)標(biāo)記“?”.使用參數(shù)化的查詢可以大大地簡(jiǎn)化編程,而且執(zhí)行效率也比直接查詢字符串要高,也更方便,很多情況下都需要更改查詢字符串,這種方式就提供了方便,只需更改參數(shù)的值即可。例:
string strCon="server=localhost;database=Northwind;Trusted_Connection=Yes;";
SqlConnection con=new SqlConnection(strCon);
con.Open();
string strqry="select * from Categories where CategoryID=@CategoryID"; //帶參數(shù)的查詢
SqlCommand cmd=new SqlCommand(strqry,con);
cmd.Parameters.Add("@CategoryID",SqlDbType.Int,4); //給參數(shù)賦于同數(shù)據(jù)庫(kù)中相同的類型
cmd.Parameters["@CategoryID"].Value="3"; //給參數(shù)賦值,可靈活改變
SqlDataReader r=cmd.ExecuteReader();
while(r.Read())
{
Response.Write(r.GetString(2)+"
"); //取出指定參數(shù)列的值
}
con.Close(); //切記關(guān)閉
使用存儲(chǔ)過(guò)程進(jìn)行查詢
先看段存儲(chǔ)過(guò)程的形式:create procedure cateproc (@CategoryID int(4)) as select * from Categories where CategoryID=@CategoryID? return。
這個(gè)是數(shù)據(jù)庫(kù)中的存儲(chǔ)過(guò)程實(shí)現(xiàn)方式,要在程序中調(diào)用存儲(chǔ)過(guò)程,一種方法是使用Command對(duì)象的 CommandType屬性來(lái)實(shí)現(xiàn)。CommandType有三個(gè)枚舉值:Text,TableDirect,StoredProcedure。只需將CommandType屬性設(shè)為第三個(gè)值即可實(shí)現(xiàn)調(diào)用存儲(chǔ)過(guò)程。例:
string strCon="server=localhost;database=Northwind;Trusted_Connection=Yes;";
SqlConnection con=new SqlConnection(strCon);
con.Open();
SqlCommand cmd=con.CreateCommand();
cmd.CommandText="cateproc";
cmd.CommandType=CommandType.StoredProcedure;
cmd.Parameters.Add("@CategoryID",SqlDbType.Int,4);
cmd.Parameters["CategoryID"].Value="2";
SqlDataReader r=cmd.ExecuteReader();
while(r.Read())
{
Response.Write(r.GetString(2)+"
");
}
con.Close();
其實(shí)在程序中實(shí)現(xiàn)調(diào)用存儲(chǔ)過(guò)程的方式跟參數(shù)化查詢很類似,有點(diǎn)舊鞋翻新的味道。
cmd.CommandType=CommandType.StoredProcedure;這種方式有個(gè)缺點(diǎn),就是當(dāng)要查詢的表,視圖或存儲(chǔ)過(guò)程的名稱中有特殊的字符(如空格)的話,則將無(wú)法識(shí)別。因此還有一種方式就是:
cmd.CommandText="{Call cateproc(?)}"; //這里是調(diào)用存儲(chǔ)過(guò)程,問(wèn)號(hào)為參數(shù)
cmd.CommandType=CommandType.Text; //關(guān)鍵是這里。
設(shè)置命令執(zhí)行超時(shí)
命令超時(shí)是指Command對(duì)象在等待結(jié)果的時(shí)間,(默認(rèn)為30秒)如果在30秒內(nèi)沒(méi)執(zhí)行查詢,則Command拋出一個(gè)異常。也可以自己進(jìn)行設(shè)置。例:cmd.CommandTimeout=60;
取消執(zhí)行查詢
有時(shí)因某種原因,需要臨時(shí)取消命令的執(zhí)行,可調(diào)用Command對(duì)象的Cancel()方法來(lái)退出執(zhí)行,如果在未執(zhí)行查詢之前,Cancel()將不做任何事。
首先,ADO.NET中使用了DataAdapter 來(lái)處理與數(shù)據(jù)庫(kù)的聯(lián)機(jī)與脫機(jī)。當(dāng)時(shí)開(kāi)發(fā)人員設(shè)計(jì)了DataAdapter是為了能夠處理脫機(jī)數(shù)據(jù),方便操作,關(guān)于這一點(diǎn),只要調(diào)用其Fill()方法即可,這時(shí)會(huì)在DataSet中創(chuàng)建一個(gè)新的名為“Table“的DataTable.要重新指定名可用DataAdapter.Fill(DataSet,“Tabelname“)。此時(shí)connection也關(guān)閉了。DataAdapter既可以用來(lái)提交查詢,并將結(jié)果存儲(chǔ)到DataSet中,也可以用來(lái)向數(shù)據(jù)庫(kù)傳遞更改。僅僅使用其Update方法即可達(dá)到向數(shù)據(jù)庫(kù)提交存儲(chǔ)地DatSet中的更改。
DataAdapter將查詢的結(jié)果存儲(chǔ)在DataSet或DataTable對(duì)象中,當(dāng)執(zhí)行這一過(guò)程的時(shí)候,DataAdapter使用了一個(gè)Command來(lái)與數(shù)據(jù)庫(kù)通訊,并在內(nèi)部使用了DataReader來(lái)獲取查詢結(jié)果,最后才將結(jié)果復(fù)制到DataSet新行中去。這也是Fill的過(guò)程。如果有兩個(gè)DataAdapter對(duì)象,都使用相同的Connection對(duì)象,在創(chuàng)建的時(shí)候就會(huì)創(chuàng)建兩個(gè)Connection對(duì)象,而不是同一個(gè),這種情況的解決方案是:
SqlConnection con=new SqlConnection("server=localhost;database=Northwind;Trusted_Connection=Yes;")
SqlDataAdapter da=new SqlDataAdapter("select CategoryID,Description from Categories",con);
SqlDataAdapter da=new SqlDataAdapter("select CategoryID,Description from Customers",con);
而不是將查詢字符串,單獨(dú)寫(xiě)成一行。
有時(shí)候可能并不希望DataSet中的架構(gòu)與數(shù)據(jù)庫(kù)中的架構(gòu)相同,這種情況的解決方案之一是可以采用別名的方法,即select id as Product ID,amount as Product Amount from product;另外一種解決方案就是使用DataAdapter提供的TableMappings集合機(jī)制,通過(guò)它就可以將查詢結(jié)果映射到DataSet結(jié)構(gòu)中,這種方法更方便,更靈活。TableMappings屬性返回一個(gè)DataTableMappingsCollection對(duì)象,其中包含了一組DataTableMappings,只要DataSet中相應(yīng)的表名稱與數(shù)據(jù)庫(kù)中的表名相同,即可以使用它來(lái)創(chuàng)建一個(gè)映射(DataSet中可以有多個(gè)表)。TableMappings里還有一個(gè)ColumnMappings屬性,其用法與TableMappings相似。其原理是DataAdapter從數(shù)據(jù)庫(kù)讀取數(shù)據(jù)后,利用DataReader從結(jié)果集中獲取列名稱,有一點(diǎn)特別要注意,即只能獲取列名稱而無(wú)法獲取表名稱,DataAdapter事先假定表名稱為T(mén)able,接著遇到映射語(yǔ)句則進(jìn)行表映射。不說(shuō)了,看代碼:
DataColumnMapping colMap;
SqlConnection con=new SqlConnection("server=localhost;database=Northwind;Trusted_Connection=Yes;");
SqlDataAdapter da=new SqlDataAdapter("select CategoryID,Description from Categories",con);
DataSet ds=new DataSet();
DataTableMapping tblMap=da.TableMappings.Add("Table","Ca"); //這里Table為關(guān)鍵,映射表名為Ca
colMap=tblMap.ColumnMappings.Add("CategoryID","ID"); //映射列表
colMap=tblMap.ColumnMappings.Add("Description","描述");
// Response.Write(tblMap.DataSetTable.ToString());
da.Fill(ds);
DataTable dt=ds.Tables["Ca"]; //這里是映射后的表名,如果仍為數(shù)據(jù)庫(kù)的表名,則無(wú)效,特別注意
this.DataGrid1.DataSource=dt;
this.DataGrid1.DataBind();
運(yùn)行代碼后就會(huì)發(fā)現(xiàn)DataGrid1上的列名為ID和描述 (^_^)
(注:使用DataTableMapping 之前要前導(dǎo)入名空間System.Data.Common;)
還可以使用AddRange方法來(lái)簡(jiǎn)化表和列的映射:(一些代碼同上面)
.......
DataTableMapping tblMap=da.TableMappings.Add("Table","Ca"); DataColumnMapping[] colMapArray=new DataColumnMapping[]{new DataColumnMapping("CategoryID","產(chǎn) 品號(hào)"),new DataColumnMapping("Description","描述")}; tblMap.ColumnMappings.AddRange(colMapArray);
......
這種映射關(guān)系只能從數(shù)據(jù)庫(kù)中讀取展示給用戶,如果要將映射后的Table的更改提交給數(shù)據(jù)庫(kù),這時(shí)庫(kù)發(fā)現(xiàn)其中列與庫(kù)中列不同,便會(huì)發(fā)生異常,DataAdapter 同時(shí)也提供了MissingMappingAction屬性來(lái)處理。
DataAdapter1.MissingMappingAction=MissingMappingAction.Passthrough/Ignore/Error
它接受MissingMappingAction的枚舉值,Passthrough這個(gè)值表示如果在庫(kù)中找不到與DataSet中相同列的話,就將此列映射到庫(kù)中,Ignore枚舉值表示忽略示出現(xiàn)的列,Error表示找不到相應(yīng)的列則拋出異常。
分頁(yè):
分頁(yè)在應(yīng)用中是常有的事,而DataAdapter本身也提供了分頁(yè)的簡(jiǎn)單功能,如:DataAdapter1.Fill(dataset,startrow,rownum,“tablename“)這種功能用于數(shù)據(jù)量較小的查詢就可以,但當(dāng)有大量數(shù)據(jù)的時(shí)候,就會(huì)發(fā)現(xiàn)這種分頁(yè)的問(wèn)題的存在。它的原理是假如有一百行數(shù)據(jù),分成十頁(yè),每頁(yè)十行,當(dāng)獲取每一頁(yè)的時(shí)候,返回前10行,再接著,第二頁(yè),刪除了前10行而獲取接下的10行,在這一次中,只是為了獲取10行數(shù)據(jù),但數(shù)據(jù)庫(kù)卻返回了20行,依此類推,第10頁(yè)的時(shí)候就會(huì)返回100行,而DataAdapter本身就幫我們刪除了90行,因?yàn)槲覀兛瓷先シ祷氐倪€是10行,這種性能太低。因此本篇繼續(xù)介紹另外一種性能較高的分頁(yè)方法。實(shí)際上這種分頁(yè)方法是將上一頁(yè)最后一行的鍵值存儲(chǔ)下來(lái),直接在SQL語(yǔ)句中就過(guò)濾掉了,不象前面那種到DataAdapter這邊才過(guò)濾掉。例:
con=new SqlConnection("server=localhost;database=Northwind;Trusted_Connection=Yes;");
da=new SqlDataAdapter("select top 50 CustomerID,CompanyName from Customers where CustomerID>'BOTTM'",con);
ds=new DataSet();
da.Fill(ds,"Categories");
this.DataGrid1.DataSource=ds.Tables["Categories"]; this.DataGrid1.DataBind();
con.Close();
這里假如上一頁(yè)最后一個(gè)鍵值為”BOTTM”,可以將它用參數(shù)替代掉,這樣就查出了在'BOTTM'之后的50行。這種方法實(shí)現(xiàn)簡(jiǎn)單效率也高。