ado.net详细研究

最近阅读了wrox的高效掌握 ADO.NET,有所感触,希望与大家分享。第一次写文章,不好请谅解。

第一篇:ADO.NET的概念

ADO.NET中间包含以下常见类:

· Connection

· Command

· DataAdapter

· DataReader

· DataSet

1 Connection类
数据库Connection类提供与数据库的连接。.net里面有OleDbConnection类和SqlConnection类,分别针对不同的数据库。SqlConnection针对sql server 7.0或者以上版本。

2 Command类
数据库Command类是数据库命令的封装。这个命令可以是Sql语句或者存储过程。这个类也有一个与具体提供者相关的前缀,如OleDb或者Sql。所有Command类必须实现一些属性,用来更改Command的文本和类型,参数,超时以及事务等。另外Comand必须实现一些方法,以便执行Command和返回信息。

3 DataAdapter类
DataAdarpter一般是结合DataSet使用,那DataSet“连接”到数据源中。
本质上DataAdapter是容器,含有4个预先配置好的Command实例,即SelectCommand,InsertCommand,DeleteCommand,UpdateCommand。这4个command实例将提供DataSet与数据库之间的操作。

4 DataReader类
使用DataReader可以实现对数据源中的数据高速、只向前的访问。同时DataReader是一个依赖连接的对象,这意味着你使用它的时候必须保持数据库连接是打开的。

5 DataSet类
相对复杂但是功能强大的类。后面具体介绍。

下面使用实例演示基本操作(我们使用sql server数据库)。

using System;

using System.Data;

using System.Data.SqlClient;



namespace test101

{

/// <summary>

/// Class1 的摘要说明。

/// </summary>

class Class1

{

/// <summary>

/// 应用程序的主入口点。

/// </summary>

[STAThread]

static void Main(string[] args)

{

//

// TODO: 在此处添加代码以启动应用程序

//



SqlConnection conn = new SqlConnection("server = joycode;Initial Catalog = Northwind; User Id = sa;Password = 87345587;");
conn.Open();
//上面两行代码新建一个SqlConnection对象conn,并把数据库连接字符串赋给其构造函数并通过Open方法打开数据库连接。

SqlCommand cmd = conn.CreateCommand();//通过conn的CreateCommand方法建立一个SqlCommand
cmd.CommandText = "select top 5 * from Customers";//设置cmd对象的命令是读取数据库汇总前5条信息
cmd.CommandType = CommandType.Text;//设置cmd的类型是sql语句,也就是默认的类型
//当然我们可以使用cmd.CommandType = CommandType.StoredProcedure指定命令类型为存储过程。
//下面的代码使用cmd的ExecuteReader方法新建一个SqlDataReader对象。
//注意:DataReader没有自己的构着函数,只能通过cmd的ExecuteReader新建。
SqlDataReader reader = cmd.ExecuteReader(CommandBehavior.CloseConnection);
string output;
while(reader.Read())
{
output = string.Format("Customer {0} : {1} works for {2}",
reader.GetString(0),reader.GetString(1),reader.GetString(2));//读取信息并显示。后面我们将专门介绍DataReader类
Console.WriteLine(output);
}
}
}
}


界面如下:

这次我们详细研究DataReader。我个人最喜欢的就是DataReader,虽然它不如DataSet强大,但是在很多情况下我们须要的是灵活的读取数据而不是大量的在内存里面缓存数据。比如在网络上每个用户都缓存大量的dataset,这很可能导致服务器内存不足。另外dataReader尤其适合读取大量的数据,因为它不在内存中缓存数据。

由于下面的讨论都设计到数据库操作,我们虚拟一个小项目:个人通讯录(单用户),这意味着我们须要一个contract的数据库,包含admin和fridend:

admin :Aname,Apassword

friend :Fname,Fphone,Faddress,Fid(主键)

当然,你可以根据自己的须要设计friend表,比如添加Fsex等字段,这里不详细列举。对应数据库建立文件:

if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[admin]') and OBJECTPROPERTY(id, N'IsUserTable') = 1)

drop table [dbo].[admin]

GO



if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[friend]') and OBJECTPROPERTY(id, N'IsUserTable') = 1)

drop table [dbo].[friend]

GO



CREATE TABLE [dbo].[admin] (

[Aname] [varchar] (8) COLLATE Chinese_PRC_CI_AS NOT NULL ,

[Apassword] [varchar] (16) COLLATE Chinese_PRC_CI_AS NOT NULL

) ON [PRIMARY]

GO



CREATE TABLE [dbo].[friend] (

[Fid] [int] IDENTITY (1, 1) NOT NULL ,

[Fname] [varchar] (8) COLLATE Chinese_PRC_CI_AS NOT NULL ,

[Fphone] [varchar] (12) COLLATE Chinese_PRC_CI_AS NULL ,

[Faddress] [varchar] (100) COLLATE Chinese_PRC_CI_AS NULL

) ON [PRIMARY]

GO

在讨论DataReader之前我们必须了解Connection和Command,虽然前面我们已经简短的介绍过了。

以下的所有讨论都针对Sql Server2000,使用的命名空间为System.Data.SqlClient。当然如果你须要使用OleDb,那是很方便的(基本上是把Sql替换成为OleDb就可以了)。

1, SqlConnection类

连接Sql server首先必须实例化一个SqlConnection对象:

SqlConnection Conn = new SqlConnection(ConnectionString);

Conn.Open();

或者

SqlConnection Conn = new SqlConnection();

Conn.ConnectionString = ConnectionString;

Conn.Open();

我比较喜欢前者,但是当你须要重新使用Connection对象去连接另外的数据库的时候,第二种方法非常有效(不过这种机会很少,一般来说一个小型系统只对应一个数据库——个人认为^_^)。

SqlConnection Conn = new SqlConnection();

Conn.ConnectionString = ConnectionString1;

Conn.Open();

//do something

Conn,Close();

Conn.ConnectionString = ConnectionString2;

Conn.Open();

//do something else

Conn,Close();

注意只有关闭一个连接以后才能使用另外的连接。

如果你不清楚Connection对象的状态,可以使用State属性,它的值为Open或者Closed,当然也还有其他值如Executing或者Broken,但是Sql server等当前版本都不支持。

If(Conn.State == ConnectionState.Open)

Conn.Colse();

上面一直提到ConnectionString,一般连接sql server的字符串为:

Data source = serverName;Initial catalog =contract;user id =sa;password= yourpassword;

如果你的sql server使用的是windows集成密码,则是:

Data source = serverName;Initial catalog = contract;Integrated Security=SSPI;

至于其他的oledb或者odbc连接串可以到http://www.connectionstrings.com

连接上数据库以后一定记得关闭连接,在ado.net中当Connection对象超出范围时连接不会自动关闭。

打开数据库连接以后我们要执行命令,所以我们讨论一下Command类

2 SqlCommand类

建立数据库连接以后我们须要访问和操作数据库——CRUD:Create、Read、Update、Delete。

为了执行命令我们创建Command对象,Comand对象要求执行Connection对象和CommandText对象。

SqlCommand cmd = new SqlCommand();

cmd.Connection = ConnectionObject;//比如我们先前建立的Conn对象

cmd.CommandText = CommandText;//比如一个Select语句

string CommandText = “Select * from friend”;

当然我们也可以使用存储过程,这个以后讨论。

另外的方法:

SqlCommand cmd = new SqlCommand(CommandText);

cmd.Connection = ConnectionObject;//比如我们先前建立的Conn对象

或者

SqlCommand cmd = new SqlCommand(CommandText,ConnecionObject);

另外还有一个包含三个参数的构造函数,我们不讨论。设计到的是事务处理。

有了Command对象以后我们须要执行操作,但是执行前请一定记得打开你的数据库连接,否则会有异常。SqlCommand对象提供下面4个执行方法:

l ExecuteNonQuery

l ExecuteScalar

l ExecuteReader

l ExecuteXmlReader

ExecuteNonQuery方法执行不返回结果的命令,通常使用它执行插入、删除、更新操作。

例如我们对contract数据库进行操作:

string sqlIns = “insert [friend] (Fname,Fphone,Faddress) values (‘雪冬寒’,’027-87345555’,’武汉大学宏博公寓’);

cmd.CommandText = sqlIns;

cmd.ExecuteNonQuery();

string sqlUpdate = “update [frined] set Faddress = ‘武汉大学’ where Fid = 1”;

cmd.CommandText = sqlUpdate;

cmd.ExecuteNonQuery();

string sqlDel = “delete from [friend] where Fid = 1;

cmd.CommandText = sqlDel;

cmd.ExecuteNonQuery();

注:如果你要测试以上代码,请自己书写,不要复制和粘贴,这样会存在文字错误(汉语和英语的符号问题)。

ExecuteScalar方法执行返回单个值的命令,例如我们须要统计系统中所有联系人的数量,就可以这样:

SqlCommand cmd = new SqlCommand(“select count(*) from friend”, Conn);

Conn.open();

int friendCount = (int) cmd.ExecuteScalar();

MessageBox.Show(“共” + friendCount.ToString() + “个联系人”);

说明:命令可以返回多个结果,此时ExecuteScalar方法将返回第一行第一个字段的值,同时其他所有值不可访问,这意味着如果象获得最好的性能,您应该构造适当的select查询,以便查询的结果集中尽可能少的包含额外的数据。如果只对单个返回值感兴趣,这个方法是首选的方法。另外该方法返回object类型数据,所有保证进行适当的类型转换是您的责任,否则您将得到异常。

ExecuteXmlReader方法执行返回一个xml字符串的命令。它将返回一个包含所返回的sml的System.Xml.XmlReader对象。这个方法我一无所知,不作讨论^_^。

DataReader类

1. 创建DataReader对象

前面提到过没有构造函数创建DataReader对象。通常我们使用Command类的ExecuteRader方法来创建DataReader对象:

SqlCommand cmd = new SqlCommand(commandText,ConnectionObject)

SqlDataReader dr = cmd.ExecuteReader();

DataReader类最常见的用法就是检索Sql查询或者存储过程返回的记录。它是连接的只向前和只读的结果集,也就是使用它时,数据库连接必须保持打开状态,另外只能从前往后遍历信息,不能中途停下修改数据。

注意:DataReader使用底层的连接,连接是它专有的,这意味这DataReader打开时不能使用对应连接进行去他操作,比如执行另外的命令等。使用完DataReader后一定记得关闭阅读器和连接。

2. 使用命令行为指定DataReader的特征

前面我们使用cmd.ExecuteReader()实例化DataReader对象,其实这个方法有重载版本,接受命令行参数,这些参数应该时Commandbehavior枚举:

SqlDataRader dr = cmd.ExecuteReader(CommandBehavior.CloseConnection);

上面我们使用的是CommandBehavior.CloseConnection,作用是关闭DataReader的时候自动关闭对应的ConnectionObject。这样可以避免我们忘记关闭DataReader对象以后关闭Connection对象。别告诉我你不喜欢这个参数,你能保证你记得关闭连接。万一你忘记了呢?又或者你使用你的partner开发的组件来进行开发呢?这个组件并不一定让你有关闭连接的权限哦。另外CommandBehavior.SingleRow可以使结果集返回单个行,CommandBehavior.SingleResult返回结果为多个结果集的第一个结果集。当然Commandbehavior枚举还有其他值,请参见msdn。

3. 遍历DataReader中的记录

当ExecuteReader方法分会DataReader对象时,当前光标的位置时第一条记录的前面。必须调用数据阅读器的Read方法把光标移动到第一条记录,然后第一条记录就是当前记录。如果阅读器包含的记录不止一条,Read方法返回一个bool值true。也就是说Read方法的作用是在允许范围内移动光标位置到下一记录,有点类似rs.movenext,不是吗?如果当前光标指示着最后一条记录,此时调用Read方法得到false。我们经常这样做:

While(dr.Reader())

{

//do something with the current record

}

注意,如果你对每一条记录的操作可能花费比较长的时间,那么意味着阅读器将长时间打开,那么数据库连接也将维持长时间的打开状态。此时使用非连接的DataSet或许更好一些。

4. 访问字段的值

有2种方法。第一种是Item属性,此属性返回字段索引或者字段名字对应的字段的值。第二种是Get方法,此方法返回有字段索引指定的字段的值。有点难以理解,不是吗?不要紧,看例子就OK了。

Item属性

每个DataReader类都定义一个Item属性。比如现在我们有一个DataReader实例dr,对应的sql语句是select Fid,Fname from friend,则我们可以使用下面的方法取得返回的值:

object ID = dr[“Fid”];

object Name = dr[“Fname”];

或者:

object ID = dr[0];

object Name = dr[0];

注意索引总是从0开始的。另外也许您发现了我们使用的是object来定义对ID和Name,是的,Item属性返回的值是object型,但是您可以强制类型转换。

int ID = (int)dr[“Fid”];

string Name = (string)dr[“Fname”];

记住:确保类型转换的有效性是您自己的责任,否则您将得到异常。

Get方法

起始我们在第一篇文章里面已经使用过改方法了。每个DataReader都定义了一组Get方法,比如GetInt32方法把返回的字段值作为.net clr 32位证书。同上面的例子一样我们用如下方式访问Fid和Fname的值:

int ID = dr.GetInt32(0);

string Name = dr.GetString(1);

注意虽然这些方法把数据从数据源类型转化为.net数据类型,但是他们不执行其他的数据转换,比如他们不会把16位整数转换为32位的。所以您必须使用正确的Get方法。另外Get方法不能使用字段名来访问字段,也就是说上面的没有:

int ID = dr.GetInt32(“Fid”); //错误

string Name = dr.GetString(“Fname”); //错误

显然上面这个缺点在某些场合是致命的,当你的字段很多的时候,或者说你过了一段时间以后再来看你这些代码,你会觉得很难以理解!当然我们可以使用其他方法来尽量解决这个问题。一个可行的办法是使用const:

const int FidIndex = 0;

const int NameIndex = 1;

int ID = dr.GetInt32(FidIndex);

string Name = dr.GetString(NameIndex);

这个办法并不怎么好,另外一个好一些的办法:

int NameIndex = dr.GetOrdinal(“Fname”); //取得Fname对应的索引值

string Name = dr.GetString(NameIndex);

这样似乎有点麻烦,但是当须要遍历阅读器种大量的结果集的时候,这个方法很有效,因为索引只需执行一次。

int FidIndex = dr.GetOrdinal(“Fid”);

int NameIndex = dr.GetOrdinal(“Fname”);

while(dr.Read())

{

int ID = dr.GetInt32(FidIndex);

string Name = dr.GetInt32(NameIndex);

}



到目前为止,我们已经讨论了DataReader的基本操作了。至于DataReader的有些高级超作我们以后再讨论。

下次我们构建一个项目——个人通讯录(单用户版本)。在这个项目中我们将用到前面讨论的所有知识,同时在这个项目里面我将尽量是这个项目符合多层体系结构标准。
前面的文章地址:

http://dev.csdn.net/develop/article/26/26246.shtm

http://dev.csdn.net/develop/article/26/26480.shtm

http://dev.csdn.net/develop/article/26/26481.shtm

这次我们用实例演示DataReader的基本应用,当然同时包含Command以及Connection的基本操作。通过这个实例的学习我们能处理一般的数据库系统了。

WinForm的个人通讯录(vs.net2003 + sql server2000)

1. 建立数据库(前面的文章已经讨论)

2. 启动Vs.net建立contract工程并设计如下界面:




其中重要控件属性列表如下:

控件类型
Text
Name
其他

ListView

listView
显示模式为details,FullRowSelect为ture

Button
确定
btnOK
默认enable为false

Button
重填
btnRE
默认enable为false

Button
添加联系人信息
btnAdd


Button
修改选中联系人信息
btnEdit


Button
删除选中联系人信息
btnDel


TextBox

txtName
默认enable为false

TextBox

txtPhone
默认enable为false

TextBox

txtAddress
默认enable为false,Multiline为true



3. 编写代码:

首先我们在窗体加载事件里面添加liestView显示数据事件

private void Form1_Load(object sender, System.EventArgs e)

{

getInfo();

}

在getInfo方法里面我们必须把数据库里面的信息读取出来同时显示到listView里面。这时一个可行的办法是使用DataReader直接读取数据然后显示。但是我这里不想这样做,我编写一个专门的类contractDB来处理数据,这个类里面有自己的一些方法,实现对数据库的操作。

// class contractDB,封装应用程序所有对数据库的操作事件

using System;

using System.Data;

using System.Data.SqlClient;



namespace contract

{

/// <summary>

/// contractDB 的摘要说明。

/// </summary>

public class contractDB

{

string connStr = "Data Source = joycode;Initial Catalog = contract;user id = sa;password = 87345587";

//string sql;

//SqlCommand cmd;



public contractDB()

{

//

// TODO: 在此处添加构造函数逻辑

//

}



/// <summary>

/// 获得所有联系人信息

/// </summary>

/// <returns>所有联系人信息</returns>

public SqlDataReader getReader()

{

string sql = "select Fid,Fname,Fphone,Faddress from friend";

SqlConnection conn = new SqlConnection(connStr);

conn.Open();

SqlCommand cmd = new SqlCommand(sql,conn);

SqlDataReader reader = cmd.ExecuteReader(CommandBehavior.CloseConnection);

return reader;

}

}

}

我的目的很明显,我将调用getReader方法得到我须要的DataReader,因此在Form1里面的getInfo方法代码如下:

private void getInfo()

{

contractDB cdb = new contractDB();

try

{

SqlDataReader reader = cdb.getReader();

this.listView.Items.Clear();

while(reader.Read())

{

string[] subItems = new string[]

{

reader.GetInt32(0).ToString(),

reader.GetString(1),

reader.GetString(2),

reader.GetString(3)

};

this.listView.Items.Add(new ListViewItem(subItems));

}

reader.Close();

}

catch(Exception ex)

{

MessageBox.Show(ex.ToString());

}

}

上面的代码很简单,不做解释,但是注意由于我们在数据访问类没有考虑异常,那么我们在这里必须处理可能出现的异常。

类似的我们在数据访问类中添加另外的方法:添加联系人,删除联系人,修改信息。整个类文件如下:

using System;

using System.Data;

using System.Data.SqlClient;



namespace contract

{

/// <summary>

/// contractDB 的摘要说明。

/// </summary>

public class contractDB

{

string connStr = "Data Source = bineon;Initial Catalog = contract;user id = sa;password = 87345587";

//string sql;

//SqlCommand cmd;



public contractDB()

{

//

// TODO: 在此处添加构造函数逻辑

//

}



/// <summary>

/// 获得所有联系人信息

/// </summary>

/// <returns>所有联系人信息</returns>

public SqlDataReader getReader()

{

string sql = "select Fid,Fname,Fphone,Faddress from friend";

SqlConnection conn = new SqlConnection(connStr);

conn.Open();

SqlCommand cmd = new SqlCommand(sql,conn);

SqlDataReader reader = cmd.ExecuteReader(CommandBehavior.CloseConnection);

return reader;

}



/// <summary>

/// 添加新联系人

/// </summary>

/// <param name="_name">姓名</param>

/// <param name="_phone">电话</param>

/// <param name="_address">地址</param>

public void addInfo(string _name,string _phone,string _address)

{

//可以使用Command Parameter来构造sql语句以获得更好的效率和更高的安全性

string sql = "insert into friend (Fname,Fphone,Faddress) values ('";

sql += _name + "','" + _phone + "','" + _address + "')";

SqlConnection conn = new SqlConnection(connStr);

SqlCommand cmd = new SqlCommand(sql,conn);

conn.Open();

cmd.ExecuteNonQuery();

conn.Close();

}



/// <summary>

/// 修改联系人信息

/// </summary>

/// <param name="_id">需要修改的联系人的id</param>

/// <param name="_name"></param>

/// <param name="_phone"></param>

/// <param name="_address"></param>

public void changeInfo(int _id,string _name,string _phone,string _address)

{

string sql = "update friend set Fname = '" + _name + "',Fphone = '" + _phone + "',Faddress = '" + _address + "'";

SqlConnection conn = new SqlConnection(connStr);

SqlCommand cmd = new SqlCommand(sql,conn);

conn.Open();

cmd.ExecuteNonQuery();

conn.Close();

}



/// <summary>

/// 删除指定fid的联系人信息

/// </summary>

/// <param name="infoID"></param>

public void deleteInfo(int infoID)

{

string sql = "delete friend where Fid = " + infoID;

SqlConnection conn = new SqlConnection(connStr);

conn.Open();

SqlCommand cmd = new SqlCommand(sql,conn);

cmd.ExecuteNonQuery();

conn.Close();

}

}

}

关于以上类代码的说明:你可以自己写一个基类,然后上面这个类继承该基类,基类包含ExeNonQueryString等方法,这样你就不必每个方法都写建立连接等的代码。另外上面的sql语句构造建议使用Command Parameter的方法,这样效率以及安全性都比较高。

另外数据库里面的admin表没有用到,这个表是用来保存登陆信息的,你可以自己为这个程序做一个登陆提示。

整个工程代码下载:点击下载
这一次我们将把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的简单功能。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值