分享
 
 
 

ADO.NET详细研究(五)--DataReader终结篇

王朝c#·作者佚名  2006-01-09
窄屏简体版  字體: |||超大  

这一次我们将把DataReader了结,同时我们提到的有些技巧与DataReader无关但是是很基本的也很有用的技巧。

一,参数化查询

在上一篇文章发表以后不少网友提意见说代码不规范,没有对sql使用参数,这确实是很大一个漏洞,所以我在这里首先谈一下参数化查询问题。

使用参数化查询的好处:可以防止sql注入式攻击,提高程序执行效率。

针对sql server .net data Provider,我们可以使用@作为前缀标记的参数。比如:

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语句的时候使用了两个参数@CategoryID和@CategoryID。为了是参数在执行过程中获得具体值,我们使用Prarmeter对象,通过它把参数添加到Command对象上,这样就获得参数化查询。

当然上面使用的add方法有其他重载版本,比如我们可以自己定义Parameter对象然后再添加:

SqlParameter para = new SqlParameter("@CategoryID",CategoryIDValue);

cmd.Parameters.Add(para);

上面SqlParameter的构造函数也有多个重载版本。具体可以查看msdn。

注意:上面的参数必须使用@前缀,另外也不仅仅是查询才能使用参数,其他更新数据库的操作类似的都能采用参数。

上面我们给出了针对sql server参数化查询的方法,现在我们讨论在OLEDB 和ODBC中指定参数。

其实这两种Provider都不支持指定参数的方法,但是我们可以在查询中使用(?)作为占位符,去指定参数将出现的位置。

sql = "select ProductID,ProductName from Products";

sql += " where CategoryID =? and ProductID < ?";

接下来我们同样应该把Parameter对象添加到Command的Parameters集合里面,但是这个时候注意参数添加的顺序必须和你使用?的顺序相通,这个是与上面sql server .net data Provider不同的地方。

OleDbCommand cmd = new OleDbCommand(sql,conn);

cmd.Parameters.Add(“CatID”,CategoryIDValue);

cmd.Parameters.Add(“MaxProductID”,MaxProductIDValue);

如果上面添加参数的次序弄反了,那么MaxProductIDValue将被指定到第一个?那里,那么就出错了。另外上面的参数名CatID和MaxProductID无所谓,你怎么命名都可以,甚至是空串也行。

注意:上面参数名无所谓,但是添加参数的次序很重要,不能颠倒。同样的其他更新数据库的操作也支持(?)占位符。

二,使用输出参数检索数据

这种方法的前提是使用存储过程。其实对支持存储过程的DBMS比如sql server来说,其上的所有操作都应该使用存储过程,以获得更好的执行效率。

比如我现在需要在我的联系人数据库中找出一个和指定ID相同的联系人的姓名(关于联系人数据库请看我的上一篇文章),我应该怎么做呢?一个办法是使用DataReader,但是效率如何?另外也许我们可以选择更好的ExecuteScalar(),但是如果我想知道的是联系人的姓名和电话呢?ExecuteScalar()的效率确实比DataReader好,但是它只能返回单个值,这个时候它也不能满足要求。我们这里使用存储过程输出参数来解决这个问题。存储过程如下:

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

上面的关键字output指明参数是输出参数。

然后我们编写代码:

SqlConnection conn = new SqlConnection(connStr);

SqlCommand cmd = conn.CreateCommand();

cmd.CommandText = "GetInfo";

cmd.CommandType = CommandType.StoredProcedure;

上面的代码新建conn对象和cmd对象,并把cmd对象的执行命令指定为名为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参数添加的时候指定了参数为输出方向,这个就是和存储过程里面的参数方向是一致的。下面我们执行命令同时获得对应的值。

conn.Open();

cmd.ExecuteNonQuery();

string Fname = cmd.Parameters["@Fname"].Value.ToString();

string Fphone = cmd.Parameters["@Fphone"].Value.ToString();

conn.Close();

三,检索多个无关的结果集

有时候我们需要对不同的表(也可能是相同的表,但是查询内容不同)进行无关的查询,比如我想查看所有联系人的姓名,然后在查看所有联系人的住址。当然这个需要我们完全可以一个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");

while(reader.Read())

{

Console.WriteLine(reader[0].ToString() + "\t");

}

i++;

}while(reader.NextResult());

注意:由于DataReader本身向前只读的特性,您不能比较多个记录集的内容,也不能在多个记录集中来回移动。结果为多个结果集时,数据读取器定位在第一个记录集上,这个和数据读取器的read()方法不同(该方法默认在记录集之前)。

另外这个例子仅仅时演示多个无关记录集的使用方法,所以请不要追究例子的实际意义。另外当我们需要检索相关的记录信息时,多表连接查询也是一个很好的选择,也就是使用Sql join查询。

四,其他相关技术

检索二进制数据

检索二进制数据的时候我们必须把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,这时你将得到异常。另外需要注意的是读取大量二进制数据的时候比较好的办法是使用GetBytes方法。下面的代码演示如果读取logo的二进制数据。

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方法参数比较多,具体请参见msdn:

ms-help://MS.MSDNQTR.2003FEB.2052/cpref/html/frlrfsystemdatasqlclientsqldatareaderclassgetbytestopic.htm

检索模式信息

如果我们只想取得数据库表的模式信息,怎么办?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());

}

}

但是上面的效率不高,因为我们不需要读取数据集,但是程序实际上做了这个工作。一个可行的解决办法是把CommandBehavior.SchemaOnly枚举传递给ExecuteRader方法,另外的办法就是构造特殊的sql命令,比如:

Sql = “select Fid,Fname,Fphone from friend where Fid = 0”

DataReader的功能基本上就介绍完了,如果需要更详细的资料请查看msdn。下一次我们将讨论DataSet的简单功能。

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有