这一次我们将把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的简单功能。

DataReader终结篇相关推荐

  1. 看看C# 6.0中那些语法糖都干了些什么(终结篇)

    终于写到终结篇了,整个人像在梦游一样,说完这一篇我得继续写我的js系列啦. 一:带索引的对象初始化器 还是按照江湖老规矩,先扒开看看到底是个什么玩意. 1 static void Main(strin ...

  2. 分布式事务科普(终结篇)

    <分布式事务科普>是我在YiQing期间整理的一篇科普型文章,内容共计两万五千字左右,应该算是涵盖了这个领域的大多数知识点.篇幅较长,遂分为上下两篇发出.上篇为<分布式事务科普--初 ...

  3. Java异常处理终结篇——如何进行Java异常处理设计

    [本文转自于Java异常处理终结篇--如何进行Java异常处理设计] 有一句这样话:一个衡量Java设计师水平和开发团队纪律性的好方法就是读读他们应用程序里的异常处理代码. 本文主要讨论开发Java程 ...

  4. 基于 abp vNext 和 .NET Core 开发博客项目 - 终结篇之发布项目

    基于 abp vNext 和 .NET Core 开发博客项目 - 终结篇之发布项目 转载于:https://github.com/Meowv/Blog 既然开发完成了,还是拿出来溜溜比较好,本篇是本 ...

  5. OSS网页上传和断点续传(终结篇)

    有了之前OSS网页上传和断点续传(OSS配置篇)和(STSToken篇),其万事俱备只欠东风啦,此终结篇即将展示OSS上传文件及断点续传的无限魅力... 网络卡顿.延迟能续传吗?能! 关了浏览器,还能 ...

  6. android中英文混合,搜狗手机输入法Android3.3中英混输无障碍之终结篇

    搜狗手机输入法Android3.3中英混输无障碍之终结篇 (2012-10-30 18:23:42) 标签: 娱乐 本周小编对于搜狗.百度两款手机输入法在中英文混输功能上进行了两轮测试,通过测试我们会 ...

  7. Linux调度系统全景指南(终结篇)

    点击上方蓝字关注公众号,更多经典内容等着你 | 导语本文主要是讲Linux的调度系统, 本篇是终结篇,主要讲当前多核系统调度策略和调度优化,调度可以说是操作系统的灵魂,为了让CPU资源利用最大化,Li ...

  8. 原创 | 分布式事务科普(终结篇)

    点击上方"朱小厮的博客",选择"设为星标" 后台回复"高效Java"领取<Effective Java第三版> 当当优惠码:V5 ...

  9. Linux C基础笔记(4)终结篇

    Linux C基础笔记(4)终结篇 补充申明:Linux C基础笔记共分为四部分,第一部分是Linux下基本命令,以及vi编辑器的使用还有C中的数据类型,第二部分为运算符.常量变量和输入输出.第三部分 ...

最新文章

  1. C++中getline函数的使用
  2. MySQL服务的启动和停止
  3. Magicodes.Sms短信库的封装和集成
  4. 解决:com.mysql.jdbc.MysqlDataTruncation: Data truncation: Data too long for column ‘ip‘ at row 1
  5. Nginx 配置https证书认证
  6. 泛微oa系统什么框架_泛微移动办公OA系统走进江苏国曜信息科技有限公司
  7. sysbench的框架实现介绍 1
  8. 内网使用 IPV6 之 TunnelBroker隧道(6in4)篇
  9. 开源协议的比较(BSD,Apache,GPL,LGPL,MIT)
  10. 什么是 PaaS?“平台即服务“ 简介
  11. 《经济学的思维方式》内容
  12. 和ChatGPT的一番对话
  13. uniapp之webscoket聊天 文字/图片/表情/语音
  14. java显示数据库_java查询数据库中的数据并显示
  15. 上班需要打卡吗?(开通微信公众号--乘着风筝去火星)
  16. 科研日志——使用opencv提取视频中的人脸
  17. Ubuntu_DNS配置过程记录
  18. 分析实时嵌入式系统软件调试问题
  19. 毕业设计-基于微信小程序的在线考试系统
  20. 计算机组成的相关资料,计算机组成原理资料

热门文章

  1. java复制重命名,JAVA操作文件的复制和重命名失败的原因
  2. matlab 仿真钢琴,用Matlab模拟钢琴的声音
  3. Spring Boot基础学习笔记12:组件注册整合Servlet三大组件
  4. 【codevs2455】繁忙的都市
  5. 复数基础——复数的绝对值,复数的极坐标形式的直观解释练习_8
  6. iphone退款申请教程_【揭秘】朋友圈卖的iOS退款、王者荣耀0元撸点券教程
  7. 【英语学习】【English L06】U08 News L2 Have you read the news about the light show?
  8. Intel 64/x86_64/IA-32/x86处理器 - SIMD指令集 - SSE扩展(11) - 数据可缓存性控制指令
  9. Intel 64/x86_64/IA-32/x86处理器 - SIMD指令集 - SSE扩展(4) - 数据传输指令
  10. Unity中的场景切换