明輝手游網(wǎng)中心:是一個免費提供流行視頻軟件教程、在線學(xué)習(xí)分享的學(xué)習(xí)平臺!

ADO.NET詳細(xì)研究(5)--DataReader終結(jié)篇

[摘要]這一次我們將把DataReader了結(jié),同時我們提到的有些技巧與DataReader無關(guān)但是是很基本的也很有用的技巧。一,參數(shù)化查詢在上一篇文章發(fā)表以后不少網(wǎng)友提意見說代碼不規(guī)范,沒有對sql使用參數(shù),這確實是很大一個漏洞,所以我在這里首先談一下參數(shù)化查詢問題。使用參數(shù)化查詢的好處:可以防止sql...
這一次我們將把DataReader了結(jié),同時我們提到的有些技巧與DataReader無關(guān)但是是很基本的也很有用的技巧。
一,參數(shù)化查詢
在上一篇文章發(fā)表以后不少網(wǎng)友提意見說代碼不規(guī)范,沒有對sql使用參數(shù),這確實是很大一個漏洞,所以我在這里首先談一下參數(shù)化查詢問題。
使用參數(shù)化查詢的好處:可以防止sql注入式攻擊,提高程序執(zhí)行效率。
針對sql server .net data Provider,我們可以使用@作為前綴標(biāo)記的參數(shù)。比如:
const string connStr = "Data source=bineon;user=sa;password=test;initial catalog=northwind;";
string sql = "select ProductID,ProductName from Products";
sql += " where CategoryID = @CategoryID and ProductID < @CategoryID ";
SqlConnection conn = new SqlConnection(connStr);
SqlCommand cmd = new SqlCommand(sql,conn);
cmd.Parameters.Add("@CategoryID",CategoryIDValue);
cmd.Parameters.Add("@MaxProductID",MaxProductIDValue);
conn.Open();
SqlDataReader reader = cmd.ExecuteReader();
上面的代碼段在定義sql語句的時候使用了兩個參數(shù)@CategoryID和@CategoryID。為了是參數(shù)在執(zhí)行過程中獲得具體值,我們使用Prarmeter對象,通過它把參數(shù)添加到Command對象上,這樣就獲得參數(shù)化查詢。
當(dāng)然上面使用的add方法有其他重載版本,比如我們可以自己定義Parameter對象然后再添加:
SqlParameter para = new SqlParameter("@CategoryID",CategoryIDValue);
cmd.Parameters.Add(para);
上面SqlParameter的構(gòu)造函數(shù)也有多個重載版本。具體可以查看msdn。
注意:上面的參數(shù)必須使用@前綴,另外也不僅僅是查詢才能使用參數(shù),其他更新數(shù)據(jù)庫的操作類似的都能采用參數(shù)。
上面我們給出了針對sql server參數(shù)化查詢的方法,現(xiàn)在我們討論在OLEDB 和ODBC中指定參數(shù)。
其實這兩種Provider都不支持指定參數(shù)的方法,但是我們可以在查詢中使用(?)作為占位符,去指定參數(shù)將出現(xiàn)的位置。
sql = "select ProductID,ProductName from Products";
sql += " where CategoryID =? and ProductID < ?";
接下來我們同樣應(yīng)該把Parameter對象添加到Command的Parameters集合里面,但是這個時候注意參數(shù)添加的順序必須和你使用?的順序相通,這個是與上面sql server .net data Provider不同的地方。
OleDbCommand cmd = new OleDbCommand(sql,conn);
cmd.Parameters.Add(“CatID”,CategoryIDValue);
cmd.Parameters.Add(“MaxProductID”,MaxProductIDValue);
如果上面添加參數(shù)的次序弄反了,那么MaxProductIDValue將被指定到第一個?那里,那么就出錯了。另外上面的參數(shù)名CatID和MaxProductID無所謂,你怎么命名都可以,甚至是空串也行。
注意:上面參數(shù)名無所謂,但是添加參數(shù)的次序很重要,不能顛倒。同樣的其他更新數(shù)據(jù)庫的操作也支持(?)占位符。

二,使用輸出參數(shù)檢索數(shù)據(jù)
這種方法的前提是使用存儲過程。其實對支持存儲過程的DBMS比如sql server來說,其上的所有操作都應(yīng)該使用存儲過程,以獲得更好的執(zhí)行效率。
比如我現(xiàn)在需要在我的聯(lián)系人數(shù)據(jù)庫中找出一個和指定ID相同的聯(lián)系人的姓名(關(guān)于聯(lián)系人數(shù)據(jù)庫請看我的上一篇文章),我應(yīng)該怎么做呢?一個辦法是使用DataReader,但是效率如何?另外也許我們可以選擇更好的ExecuteScalar(),但是如果我想知道的是聯(lián)系人的姓名和電話呢?ExecuteScalar()的效率確實比DataReader好,但是它只能返回單個值,這個時候它也不能滿足要求。我們這里使用存儲過程輸出參數(shù)來解決這個問題。存儲過程如下:
CREATE PROCEDURE GetInfo
(
@FID int,
@Fname varchar(8) output,
@Fphone varchar(12) output
)
AS
Select @Fname = Fname,@Fphone = Fphone
from friend
where Fid = @Fid
GO
上面的關(guān)鍵字output指明參數(shù)是輸出參數(shù)。
然后我們編寫代碼:
SqlConnection conn = new SqlConnection(connStr);
SqlCommand cmd = conn.CreateCommand();
cmd.CommandText = "GetInfo";
cmd.CommandType = CommandType.StoredProcedure;
上面的代碼新建conn對象和cmd對象,并把cmd對象的執(zhí)行命令指定為名為GetInfo的存儲過程。接下來我們需要給cmd對象的Parameters集合添加Parameter對象了。
SqlParameter param = cmd.Parameters.Add("@Fid",16);
param = cmd.Parameters.Add("@Fname",SqlDbType.VarChar,8);
param.Direction = ParameterDirection.Output;
param = cmd.Parameters.Add("@Fphone",SqlDbType.VarChar,8);
param.Direction = ParameterDirection.Output;
我們注意到了上面的@Fname和@Fphone參數(shù)添加的時候指定了參數(shù)為輸出方向,這個就是和存儲過程里面的參數(shù)方向是一致的。下面我們執(zhí)行命令同時獲得對應(yīng)的值。
conn.Open();
cmd.ExecuteNonQuery();
string Fname = cmd.Parameters["@Fname"].Value.ToString();
string Fphone = cmd.Parameters["@Fphone"].Value.ToString();
conn.Close();
三,檢索多個無關(guān)的結(jié)果集
有時候我們需要對不同的表(也可能是相同的表,但是查詢內(nèi)容不同)進(jìn)行無關(guān)的查詢,比如我想查看所有聯(lián)系人的姓名,然后在查看所有聯(lián)系人的住址。當(dāng)然這個需要我們完全可以一個sql語句搞定,也不需要所謂的多個記錄集,但是請允許我以這個需求來演示多個記錄集的操作。
多個查詢語句之間使用;分開。具體代碼如下:
SqlConnection conn = new SqlConnection(connStr);
SqlCommand cmd = conn.CreateCommand();
string sqla = "select Fname from friend";
string sqlb = "select Fphone from friend";
cmd.CommandText = sqla + ";" + sqlb;
然后我們可以和以往一樣獲得DataReader,但是由于是多個記錄集,我們讀取完第一個記錄集以后如果使用下一個記錄集呢?答案是NextResult()方法,該方法為bool類型,如果有下一個記錄集就返回真,否則為假。
conn.Open();
SqlDataReader reader= cmd.ExecuteReader();
int i = 1;
do
{
Console.WriteLine("第" + i.ToString() + "個記錄集內(nèi)容如下:\n");
while(reader.Read())
{
Console.WriteLine(reader[0].ToString() + "\t");
}
i++;
}while(reader.NextResult());
注意:由于DataReader本身向前只讀的特性,您不能比較多個記錄集的內(nèi)容,也不能在多個記錄集中來回移動。結(jié)果為多個結(jié)果集時,數(shù)據(jù)讀取器定位在第一個記錄集上,這個和數(shù)據(jù)讀取器的read()方法不同(該方法默認(rèn)在記錄集之前)。
另外這個例子僅僅時演示多個無關(guān)記錄集的使用方法,所以請不要追究例子的實際意義。另外當(dāng)我們需要檢索相關(guān)的記錄信息時,多表連接查詢也是一個很好的選擇,也就是使用Sql join查詢。
四,其他相關(guān)技術(shù)
檢索二進(jìn)制數(shù)據(jù)
檢索二進(jìn)制數(shù)據(jù)的時候我們必須把CommandBehavior.SequentialAccess枚舉值傳遞給ExecuteReader方法。另外就是要注意從 記錄集讀取信息的時候必須按照你的sql語句的順序讀取。比如:
Sql = “select pub_id,pr_info,logo from pub_info where pub_id=’0763’ “;
那么你讀取的時候必須先取得pub_id,然后才是pr_info,再接著時logo,如果你讀取了pr_info以后又想讀取pub_info,這時你將得到異常。另外需要注意的是讀取大量二進(jìn)制數(shù)據(jù)的時候比較好的辦法是使用GetBytes方法。下面的代碼演示如果讀取logo的二進(jìn)制數(shù)據(jù)。
System.IO.MemoryStream stream = new System.IO.MemoryStream();
System.IO.BinaryWriter writer = new System.IO.BinaryWriter(stream);
int BufferSize = 1024;
byte[] Buffer = new Byte[BufferSize];
long Offset = 0;
long BytesRead = 0;
do
{
BytesRead = reader.GetBytes(2,Offset,Buffer,0,BufferSize);
writer.Writer(Buffer,0,(int)BytesRead);
writer.Flush();
Offset += BytesRead;
}
while(BytesRead == BufferSize);
其中GetBytes方法參數(shù)比較多,具體請參見msdn:
ms-help://MS.MSDNQTR.2003FEB.2052/cpref/html/frlrfsystemdatasqlclientsqldatareaderclassgetbytestopic.htm
檢索模式信息
如果我們只想取得數(shù)據(jù)庫表的模式信息,怎么辦?DataReader的GetSchemaTable方法可以滿足我們的要求。
string sql = "select Fid,Fname,Fphone from friend";
cmd.CommandText = sql;
conn.Open();
SqlDataReader reader = cmd.ExecuteReader();
DataTable SchemaTable = reader.GetSchemaTable();
然后我們可以遍歷DataTable獲得所有模式信息
DataRowCollection SchemaColumns = SchemaTable.Rows;
DataColumnCollection SchemaProps = SchemaTable.Columns;
foreach(DataRow SchemaColumn in SchemaColumns)
{
foreach(DataColumn SchemaColumnProp in SchemaProps)
{
Console.WriteLine(SchemaColumnProp.ColumnName + "=" + SchemaColumn[SchemaColumnProp.ColumnName].ToString());
}
}
但是上面的效率不高,因為我們不需要讀取數(shù)據(jù)集,但是程序?qū)嶋H上做了這個工作。一個可行的解決辦法是把CommandBehavior.SchemaOnly枚舉傳遞給ExecuteRader方法,另外的辦法就是構(gòu)造特殊的sql命令,比如:
Sql = “select Fid,Fname,Fphone from friend where Fid = 0”

DataReader的功能基本上就介紹完了,如果需要更詳細(xì)的資料請查看msdn。下一次我們將討論DataSet的簡單功能。