任务3. 2 ADO. NET实现图书信息的批量读取
图3.2 Ado.NET结构
2)ADO.NET的数据库操作过程
ADO.NET对数据库的操作大致可分为3个步骤:连接到数据库;执行数据库操作,或直接对数据库进行更新,或检索数据到DataSet数据集(内存);关闭连接。第二步是关键步骤,对于数据更新或数据检索两种不同的数据操作,所使用的ADO.NET对象不完全相同,数据操作过程也有差异,数据更新或数据检索过程如图3.3、图3.4所示。
数据更新(Insert,Update,Delete)过程如图3.3所示,此种操作也称为单向操作,见图示箭头。
图3.3 ADO.NET数据更新过程
数据检索(Select)过程如图3.4所示,此种操作也称为双向操作,见图示箭头。
图3.4 ADO.NET数据检索过程
3).NET Framework数据提供程序类详解
目前,市场上主流的商业数据库管理系统都由不同的厂商开发,系统结构之间差异较大,尚未形成统一的标准,因此,ADO.NET不得不为每一款数据库管理系统提供一套.NET Framework数据提供程序类,为了区分它们,对于不同的数据库.NET Framework数据提供程序类使用不同的前缀;另一方面,为了不增加开发者的额外负担,这些不同前缀的数据操作类具有完全相同的使用方法。
①前缀不同
对于不同数据库系统的.NET Framework数据提供程序类具有不同的前缀,下面列出几种主流数据库系统对应的.NET Framework数据提供程序类的前缀:
a.对于SQL Server 7.0及以上版本的数据库系统,.NET Framework数据提供程序类需要加上SQL-的前缀,即SQLConnection、SQLCommand、SQLDataReader和SQLDataAdapter等;
b.对于Access数据库这种Ole类型的数据库系统,.NET Framework数据提供程序类需要加上OleDb-的前缀,即OleDbConnection、OleDbCommand、OleDbDataReader和OleDbDataAdapter等;
c.对于Oracle数据库系统,需要加上Oracle-的前缀,即OracleConnection、OracleCommand、OracleDataReader和OracleDataAdapter等;
d.对于Odbc标准类型的数据库系统,需要加上Odbc-前缀,即OdbcConnection、OdbcCom-mand、OdbcDataReader和OdbcDataAdapter等。
②命名空间不同
同理,要使用不同数据库系统的.NET Framework数据提供程序类需要引入不同的命名空间,下面列出几种主流数据库系统对应的.NET Framework数据提供程序类的命名空间:
a.SQL Server:using System.Data.SQLClient;
b.OLEDB类型:using System.Data.OleDb;
c.Oracle: using System.Data.OracleClient;
d.ODBC类型: using System.Data.Odbc。
(2)Connection连接对象
下面,将对.NET Framework数据提供程序的主要对象进行具体介绍,考虑到.NET Framework数据提供程序同微软自己的产品SQL Server和Access数据库系统结合性更好,同时,SQL Server和Acces也具有一定的典型性,因此,主要就这两种.NET Framework数据提供程序进行介绍和对比,读者在学习后就能很容易地推广到其他数据库系统。
Connection对象表达了“连接”这样一个抽象概念,用于同数据库建立连接。
1)主要属性和方法如下
ConnectionString属性:设置和获取连接字符串。
要建立同数据的连接,必须知道数据库所在的服务器,数据库名,所使用的连接方式,用于验证的用户名和密码等信息,连接字符串使用固定格式包含了这些信息,因此,建立同数据的连接必须提供连接字符串。
针对不同数据库系统的Connection对象所使用的连接字符串不同,下面列出用于SQL Server和Access的连接字符串,其他的连接字符串的格式,请读者自己上网查阅。
SQL Server身份验证:“Data Source=服务器名;Initial Catalog=数据库名;User ID=用户名;Pwd=密码”。如果没有密码可以省略Pwd项。
SQL Server的Windows集成身份验证:“Data Source=服务器名;Initial Catalog=数据库名;Integrated Security= True”。Integrated Security项的默认值为False,因此,省略表示使用SQL Server身份验证,需要用户名和密码。
Access数据库(主机处于局域网):“Provider=M icrosoft.Jet.OLEDB.4.0;Data Source=数据库所在磁盘路径”。
Open方法:打开数据库连接。
Close方法:关闭数据库连接。
注意:数据库连接是系统资源,打开使用后必须释放,否则该连接就会一直被占用。因此,Open方法和Close方法必须配对使用。
Connection对象的构造函数有两种形式,无参数的形式和带连接字符串参数的形式,以SQL Server.NET Framework数据提供程序为例:
无参数形式:SQLConnection()
带连接字符串参数形式:SQLConnection(String ConnStr)。
2)Connection的典型代码示例
典型代码片段如下:
(3)Command命令对象
Command对象表达了执行命令这样一个抽象概念,用于对数据库执行SQL标准操作。
1)主要属性和方法如下:
①Connection属性:获取或设置此次执行命令操作所使用的数据库连接对象;
②CommandText属性:获取或设置要对数据源执行的Transact-SQL命令或存储过程;
③CommandType属性:指示此次执行命令操作是执行Transact-SQL命令还是存储过程;
④Parameters属性:指示此次执行命令操作向Transact-SQL命令还是存储过程传递的参数集合;
⑤ExecuteNonQuery方法:对数据库执行不返回批量数据的单向Transact-SQL命令,并返回受影响的行数,如执行INSERT、DELETE、UPDATE;
⑥ExecuteReader方法:对数据库执行查询操作,并返回DataReader对象,使用该DataReader对象可以按先后顺序逐条记录的读取数据;
⑦ExecuteScalar方法:对数据库执行查询操作,并返回结果集中第一行第一列的值,而忽略其他列或行。用于执行一些统计查询操作,如执行COUNT()、SUM()等。
Command对象的构造函数有两种形式,以SQLServer.NETFramework数据提供程序为例:
无参数的形式:
带连接对象参数和SQL命令字符串参数(或存储过程名)的形式:
2)Command的典型代码示例
①下例用于登录页面,验证用户名和密码是否合法,典型代码片段如下:
(4)DataReader对象
DataReader对象模拟了单向只读游标的操作,用于从数据库中前向、逐行读取记录。它具有访问速度较快、单向(从前向后)、只读、服务器资源占用少等特点。
1)主要属性和方法
①HasRows属性:指示是否包含了数据库操作后返回的记录,该属性用来判断读取数据前对数据库的操作是否成功。
②Read方法:读取当前记录,判断下一条记录是否为空,为空则返回False,否则前进到下一条记录,供下次读取。
③Close方法:关闭DataReader对象。DataReader对象会占用数据库中重要的只读游标资源,因此,使用完后必须关闭。
DataReader对象无需直接创建,而是通过调用Command对象的ExecuteReader方法创建并获得的。
2)DataReader的典型代码示例
使用DataReader读取数据库操作结果数据的步骤。
①创建Command对象。
②调用ExecuteReader()方法执行查询操作,并创建和返回DataReader对象。
③使用DataReader的Read()方法逐行读取数据,直至结果记录集的末尾,如果没有下条记录,此时Read()方法返回False,据此判断是否读完所有记录。
④读取某条记录后,可通过索引的方式取得某列的数据,以字段名称为关键字索引,如(String)dataReader[“LogInId”]。
⑤关闭DataReader对象,释放资源。
典型代码片段如下:
【任务实施】
新书注册的实现步骤。
①在SQL Server 2005中的Manage Studio中,通过可视化设置的方式或执行Transact-SQL命令的方式,创建网络书店的后台数据库BookStore,在BookStore数据库下创建图书信息表BookInfo,表结构如图3.5所示。
图3.5图书信息表BookInfo表结构
图书信息表的字段包括图书信息的标识符、书名、图书类型、作者、出版社、ISBN编号、内容简介、单价、库存量、封面图片文件名、新书注册时间、图书出版时间。
②在VS 2008开发环境的“解决方案资源管理器”下的网上书店项目上右键单击,创建一个新的新书注册页面,按图3.6所示进行界面设计:使用表格布局,拖放Web输入控件,对控件属性进行设置。具体方法已在本书“项目2”中进行了详细介绍,请读者自行完成。
图3.6图书注册页面的界面设计
③使用Web验证控件对图书注册页面的输入控件,主要是对TextBox文本框控件进行验证。其中,全部的文本框控件都要进行非空验证;单价、初始库存、出版时间文本框还要进行数据类型验证,必须保证单价为货币类型,初始库存为整数类型,出版时间为时间类型。具体方法已在本书“项目2”的“验证控件部分”中进行过详细介绍,请读者根据要求自行完成。
④切换到图书注册页面对应的后台代码文件中,在Page_Load()事件处理方法中添加页面信息初始化代码,即从网上书店数据库的分类表中将图书分类名称查询出来,填充到页面上的“分类”下拉列表框,图书信息分类表的结构如图3.7所示。
图3.7图书分类表结构
图书信息分类表的结构简单,ClassId为分类标识号,ClassName为分类名。
页面信息初始化代码如下:
⑤为图书注册页面上的登记按钮生成单击事件处理方法,添加新书注册代码,即接收页面上的图书注册信息,然后写入后台数据库的图书信息表,同时将封面图片文件上传到服务器的指定目录下,关于如何上传文件已在本书“项目2”的“文件上传控件”部分进行过详细介绍,完整代码如下:
⑥单击VS 2008工具栏上的绿色箭头按钮或按快捷键“F5”启动调试运行,对程序进行调试。调试成功后,在网上书店数据库的图书分类表中输入初始化图书类别信息,测试页面初始化是否正确,能否正确注册新书信息和上传封面图片。
说明:考虑到新书注册时,如果文件上传不成功,就不能向数据库插入一条图书信息,因此使用了Try…Catch…Finally异常处理语句,当文件上传部分的代码出错时,会自动进入Catch块输出出错信息,然后进入Finally块关闭连接,而不会执行文件上传部分后面的向数据库插入图书信息部分。
【拓展与提高】
(1)Command执行存储过程
Command除能执行Transact-SQL命令,还能执行存储过程。存储过程是一组为了完成特定功能的SQL命令集,经编译后存储在数据库中,用户通过指定存储过程的名字并给出参数来执行它,存储过程经过编译,执行速度很快,还能起到一定的隐蔽信息的作用,因此在进行数据库设计时很常用。下面用一个简单实例来进行说明,该实例将前面关于用户注册的所执行的SQL命令改写成存储过程,然后使用Command对象进行调用执行。
在SQL Server 2005的Management Studio中,在左边的对象资源管理器中,展开下面的“数据库”节点,继续展开网上书店数据库名称节点,继续展开“可编程性”节点,在“存储过程”节点上右键单击,在弹出的菜单中选择“新建存储过程”。存放存储过程的SQL文件就被创建,并在编辑窗口中被自动打开。删除存储过程文件中的默认内容,输入以下内容:
存储过程名称为sp_UserRegister,包含两个数据库参数,用户名@uId和密码@uPwd,功能是向用户表中插入一条记录,将从外部接收的参数值作为记录的两个字段值。有关存储过程的格式,请复习数据库方面的课程或上网查阅。
在执行存储过程之前,需要先将参数传递给存储过程,这就需要使用Parameter对象,该对象用于给SQL语句或存储过程传递参数,实现应用程序的数据类型向数据库系统的数据类型的转换。一个Parameter对象只能和一个数据库参数建立对应关系。Parameter对象是一个.NET Framework数据提供程序的辅助对象,因此对于不同的数据库系统需要加上不同的前缀。
Parameter对象的主要属性。
①ParameterName属性:获取或设置该参数对象对应的数据库参数的名称;
②SQLDbType属性:获取或设置该参数对象与SQL Server数据库参数兼容的数据类型;
③Size属性:获取或设置该参数对象与数据库参数相同的大小;
④Value属性:获取或设置该参数对象的值。
创建Parameter对象时,经常使用的构造函数形式为:SQLParameter(String,SQLDbType,Int32)。需要传递的构造函数的3个参数分别对应于Parameter对象的前面3个属性。
通过Paramter对象传递参数的步骤。
①根据SQL语句或存储过程的参数名称和类型创建Parameter对象;
②通过设置Parameter对象的Value属性,将参数值放入Parameter对象;
③将Parameter对象加入Command对象的Parameters参数对象集合。
例如,传递用户名参数的示例代码如下:
一旦参数传递给了Command对象,Command对象就可以执行存储过程,Command对象执行存储过程的方式很简单,只需将CommandType属性设置为存储过程,将CommandText属性设置为存储过程的名称即可,示例代码如下:
使用存储过程实现用户注册实例的典型代码如下:
(2)事务操作
事务(Transaction)是用户定义的一个操作序列,这些操作形成一个不可分割的工作单位。要么全都做,要么全都不做,通过事务,SQL Server能将逻辑相关的一组操作绑定在一起,以便保持数据的完整性。例如,用户在网上书店系统购书时,首先要将购书信息写入图书销售表,然后还需要在图书信息表中减少被购图书的库存量,这就需要进行两个数据库操作,这两个操作就构成了一个事务。
ADO.NET使用SQLTransaction对象实现SQL Server数据库操作事务。SQLTransaction对象的主要属性和方法。
①Connection属性:获取与该事务关联的SQLConnection连接对象;
②Comm it方法:提交数据库事务;
③Rollback方法:从挂起状态回滚事务;
④Save方法:在事务中创建保存点指定名称(用于回滚事务的某一部分)。
1)ADO.NET实现事务操作的要点说明
SQLTransaction对象由SQLConnection连接对象的BeginTransaction方法创建并返回,该方法同时表示在该连接对象上开始一个数据库事务。
创建了SQLTransaction事务对象后,要用该事务对象作为创建SQLCommand命令对象所构造函数的参数;或在命令对象创建后,使用该事务对象设置命令对象的Transaction属性。这样才能将命令对象(代表某个数据库操作)在事务中登记并纳入事务处理的范围。
当全部命令对象执行完毕(即数据操作全部完成),就能使用SQLTransaction事务对象的Commit方法提交事务;如果其中有些操作不成功,就需要使用该事务对象的Rollback方法回滚事务。
因此,其他与事务相关的对象属性和方法。
①SQLConnection的BeginTransaction方法:开始数据库事务。
②SQLCommand的Transaction属性:获取或设置该命令对象所属的事务对象。
2)典型代码示例
【教学评价】
评价标准见表3.1。
表3.1任务3.1评价标准
任务3. 2 ADO. NET实现图书信息的批量读取
【任务描述】
在任务3.1中,已使用.NET Framework数据提供程序的对象实现少量数据的读取,如读取单值,读取单行。然而在一般情况下,数据库查询操作会返回多条记录形成的记录集合。那么,ADO.NET中如何将查询得到的批量数据一次性地从数据库中读取出来?如何对批量数据集合进行暂存,以便于应用程序进行处理和显示?
ADO.NET为此提供的解决方案是:在内存中建立一个小型的缓存数据库,称其为数据集(DataSet)对象。然后,通过.NETFramework数据提供程序的适配器(DataAdapter)对象将查询结果集一次性地搬运到数据集中。一次查询的结果集会在数据集中形成一张数据表(DataT-able),数据集中可以容纳多个数据表。一旦批量数据被放入数据集,应用程序就能脱离数据库,而只需与内存中的数据集进行数据交换。
在本任务中,将按照上述思想实现图书信息的批量读取。实施任务之前,应首先对上述解决方案所涉及的相关对象,如DataSet、DataTable、DataAdapter等的概念,重要属性、方法和事件,以及它们的编程方法进行介绍。
【知识准备】
(1)DataSet数据集对象
数据集是ADO.NET中独立于.NET Framework数据提供程序的另一个主要组件,用于在内存中临时缓冲来自数据库或其他数据源的批量数据,可以被简单地理解为内存中的一个临时数据库。数据集不依赖任何数据库或其他数据源而存在,与.NET Framework数据提供程序的对象不同,数据集对象不直接操作数据库系统,因此无需为不同的数据库系统添加前缀,始终只有DataSet这样一种形式。
数据集内部具有复合结构,这种结构与关系数据库的概念模型很相似,在理解其内部结构后,就能很容易地对数据集中的数据进行存取,数据集的内部对象结构如图3.8所示。
图3.8数据集内部对象结构图
由图3.8可知,一个DataSet对象内部有一个DataTableCollection类型的集合对象,该集合中的单个元素为一个DataTable对象。DataTable对象内部有一个DataRowCollection类型的集合对象和一个DataColumnCollection类型的集合对象,DataRowCollection集合对象的单个元素为一个DataRow对象,而DataColumnCollection集合对象的单个元素为一个DataColumn对象。
更为形象的数据集结构示例图如图3.9所示。
图3.9数据集结构示例图
由图3.9可知,一个数据集由多个数据表构成,而每个数据表包含多个数据列,这些数据列定义了数据表的表结构,类似于关系数据库中表的字段;同时,每个数据表又包含多个数据行,每个数据行表示一条完整信息,类似于关系数据库中表的记录。由此可以看出,这种结构与关系模型很相似。
DataSet及其内部对象都属于System.Data命名空间,因此在使用时要首先引用该命名空间,下面对DataSet及其内部对象进行一一介绍。
1)DataSet主要属性和方法
①DataSetName属性:用于获取或设置当前数据集的别名,该别名在作为关键字进行检索时使用;
②Tables属性:数据集中包含的表集合,可以通过索引和关键字进行检索;
③HasChanges属性:获取一个布尔值,指示数据集是否被更改了;
④Clear方法:清除数据集中包含的所有表的所有行。
DataSet构造函数:DataSet对象有两种形式构造函数,即DataSet();和DataSet(String dsName);第二种形式的参数为数据集别名字符串,第一种形式采用默认别名NewDataSet,例如:
2)DataTable主要属性、方法和事件
①Data Table主要属性
a.Colum ns属性:表示DataTable内部包含的数据列对象(DataColumn)的集合;
b.Rows属性:表示DataTable内部包含的数据行对象(DataRow)的集合;
c.Constraints属性:表示DataTable内部包含的约束集合;
d.DataSet属性:表示DataTable所属的数据集对象;
e.PrimaryKey属性:表示作为DataTable主键的DataColumn列对象,可以是多个列对象作为复合主键;
f.HasChanges属性:获取一个布尔值,指示当前DataTable是否被更改了。
②Data Table方法
a.AcceptChanges方法:提交对当前DataTable所做的所有修改;
b.NewRow方法:创建一个新的DataRow行对象,使用该方法创建行对象。
③Data Table事件
Colum nChanged事件:某数据列被修改后触发该事件;
RowChanged事件:某数据行被修改后激发该事件;
RowDeleted事件:某数据行被删除后激发该事件。
DataTable构造函数:
DataTable与DataSet的构造函数的形式类似:DataTable()和DataTable(String dtName),第二种形式更为常用,其参数为DataTable别名字符串,例如:
3)DataColumn主要属性
DataColumn对象用于定义DataTable的列,与数据库中的字段的概念相似。
①AllowDBNull属性:获取或设置一个布尔值,指示该数据列是否允许null值;
②ColumnName属性:获取或设置该数据列的名称;
③DataType属性:获取或设置该数据列的数据类型;
④DefaultValue属性:获取或设置该数据列的默认值;
⑤Table属性:表示该数据列对象所属的DataTable对象的名称;
⑥Unique属性:获取或设置一个布尔值,指示数据列对象的值是否必须唯一。
DataColumn的构造函数:
DataColumn构造函数的形式比较多,常用的有两种:DataColumn()和DataColumn(String,Type),第二种形式的参数对应为数据列的名称和数据列的数据类型。
一般情况下,不需要使用new运算符创建对象,而是使用DataTable对象的Columns属性的Add(String,Type)方法,向数据表添加数据列的同时在Add方法内部创建数据列,如:
数据列对象的作用是为数据表对象创建表结构,例如,下例创建了一个内部包含3个数据列的空的学生表(Student),各数据列的名称分别为“StuNo”(学号),“StuName”(姓名),“Stu-Marks”(成绩)。数据类型分别为整数,字符串,双精度。其中,学号不能为空,默认值为12。
4)DataRow主要属性、方法
DataRow对象用于定义DataTable的行,与数据库中记录的概念相似,表示数据表中的实际数据。
①Data Row主要属性
a.RowState属性:获取或设置一个枚举值,表示当前行对象的状态;
b.Table属性:表示此数据行所属的DataTable对象。
②Data Row方法
AcceptChanges方法:用于提交自上次调用了AcceptChanges之后对该行所做的所有修改;
Delete方法:删除当前DataRow行对象,实际是在该行对象上作一个删除标识,并不真正删除该行;
RejectChanges方法:用于拒绝提交自上次调用了AcceptChanges之后对该行所做的所有修改。
DataRow的构造函数:
DataRow的构造函数只有一种无参数的形式DataRow(),该构造函数只限于内部使用。也就是说,与创建DataColumn对象类似,DataRow对象无需显式的使用new运算符创建,而是通过调用DataTable对象的NewRow方法在其内部创建DataRow对象,如:
数据行的作用是为数据表添加数据,例如,下例在3)中创建的包含3个数据列的“学生”数据表中添加一个数据行并填充数据使用类似,能很容易地实现添加多个数据行。
5)检索数据
一旦数据集被建立并填充了数据,如何对所需要的数据进行检索,DataSet提供的方法是:从数据集对象开始,利用下标或关键字作为索引,自上而下,依次检索数据表、数据行和数据列,例如:检索schoolDS数据集中名为“Student”的数据表中的第一行中名为“StuName”列所对应的值:
String stuName= schoolDS.Tables["Student"].Rows[0]["StuName"];
结合前面的例子可知该数据集中仅有一个表,该表中仅有一行,而“StuName”列是第二列,因此可以将上述代码用下标的方式改写如下,
String stuName= schoolDS.Tables[0].Rows[0][1];
通过对比可以看出,使用关键字进行检索代码更加清晰明了,因此推荐使用第一种检索方式。
6)DataTable的主键约束
在数据库中,主键约束是一种极其常用而且重要的约束方法,而在“内存数据库”数据集的数据表中,同样能够实现主键约束。与数据库的主键约束类似,可以使用单个数据列作为主键,也可以使用多个数据列作为复合主键。例如:
从以上代码可知,数据表对象的PrimaryKey属性的数据类型为一个数据列对象数组,单列主键即列对象数组中只有一个元素。
(2)DataAdapter适配器对象
DataAdapter适配器对象是.NET Framework数据提供程序的其中一个对象,它是外存数据库与内存数据集之间的桥梁和纽带,作用是将数据库的批量数据填充到数据集,或将数据集的数据更新回数据库。DataAdapter适配器对象能自动实现数据库与数据集之间的映射:例如,将数据库查询结果(批量数据)映射成DataSet中的DataTable,将数据库表的字段映射成DataTable的DataColumn,将数据库的各种约束映射成DataSet中的各种属性约束。同时,它还能进行数据库与数据集之间的数据类型转换。例如,数据库中的nVarchar、Varchar、Char型在向数据集的映射过程中会被自动转换为String型,数据库中的日期型会被自动转换成日期型,数据库中的货币型会被自动转换成浮点型。这种数据类型转换的要点是数据库的数据类型和数据集的数据类型(即程序的数据类型)必须能够兼容。
1)主要属性和方法
①主要属性
a.SelectCommand属性:接收一个执行查询操作的Command对象,这样适配器对象才能执行从数据库填充数据集的操作;
b.InsertCommand属性:接收一个执行插入操作的Command对象,这样适配器对象才能执行从数据集提交数据库的插入操作;
c.DeleteCommand属性:接收一个执行删除操作的Command对象,这样适配器对象才能执行从数据集更新数据库的删除操作;
d.UpdateCommand属性:接收一个执行删除操作的Command对象,这样适配器对象才能执行从数据集提交数据库的删除操作。
②方法
a.Fill方法:向DataSet中的表填充数据;
b.Update方法:将DataSet中的数据提交回数据库。
DataAdapter构造函数:
DataAdapter的构造函数主要有两种形式,以SQL Server的.NET框架数据提供程序为例:SQLDataAdapter()和SQLDataAdapter(String,SQLConnection),第二种形式的第一个参数表示一个查询SQL语句,第二个参数表示当前连接对象,例如:
如果创建默认适配器对象,则需要用查询命名对象设置其SelectCommand属性,例如:
2)DataAdapter正向填充代码示例
以SQL Server的.NET框架数据提供程序为例:
3)DataAdapter的反向提交代码示例
使用DataAdapter的反向提交操作能够一次性地批量更新数据库,利用该特征,可以首先对数据集进行各种更新,当数据集的状态确定后,再一次提交给数据库。这样既能避免频繁占用数据库资源,又能有效提高运行速度。
仍以SQL Server的.NET框架数据提供程序为例,下面的代码中使用了一个.NET框架数据提供程序的辅助对象CommandBuilder,其功能是自动从正向查询操作生成反向的插入、更新、删除操作的组合操作。CommandBuilder对象会自动对执行正向操作后的数据状态和数据集的当前数据状态进行对比,根据对比结果生成反向的组合操作。这些反向操作会自动被DataAdapter对象识别,并会在反向提交时对数据库执行。
【任务实施】
任务实施步骤。
①仍然使用上一个任务建立的网上书店的后台数据库BookStore中的图书信息表BookInfo。
②在解决方案资源管理器下的网上书店项目上右键单击,创建一个新的图书信息展示页面,切换到该页面对应的.CS代码文件,在页面初始化方法Page_Load中添加批量读取图书信息的代码如下。
③若要使批量图书信息简单地显示出来,只需从“工具箱”窗口的“数据”组中拖放一个GridView Web控件到图书信息展示页面上,该控件对象的默认名称为GridView1,然后在Page_Load方法已有代码的后面添加如下代码:
GridView控件是数据控件中的一种,而数据控件是一类使用较为复杂的Web控件。有关数据控件、数据源、数据绑定的内容,将在后面的项目中进行详细介绍,运行结果如图3.10所示。
图3.10原始显示图书信息的运行效果
④若要检索批量图书信息中某一项的内容,例如,希望检索第3条图书信息的内容介绍,只需在Page_Load方法已有代码的后面添加如下代码:
运行结果如图3.11所示。
图3.11检索图书信息项的运行效果
【拓展与提高】
(1)DataView视图对象
DataView视图对象同样作为DataSet组件的一个内部对象,用作DataTable中存储的数据的表示层。DataView对象的概念和数据库的视图概念类似,用于展示DataTable中的数据的子集,为DataTable提供经过排序、筛选和搜索等多种处理后的自定义数据视图。当DataTable中的数据需要多种组合呈现形式时,就可以使用DataView对象。
DataView对象可以作为ASP.NET的数据控件的数据源,与数据控件进行数据绑定,如本任务中的图书信息的显示,就是将数据表的默认视图作为GridView控件的数据源与GridView进行数据绑定的,见以下代码:
DataTable的DefaultView属性就是获取数据表的默认视图,默认视图指呈现形式与原始数据表完全相同的视图对象。
1)主要属性和方法
①主要属性
a.RowFilter属性:用于获取或设置表达式,该表达式用于筛选能从DataView中查看的行;
b.Table属性:指视图的来源DataTable对象。
②方法
a.AddNew方法:向DataView添加新行;
b.Delete方法:用于删除DataView中指定索引的行。
构造函数:
常用的构造函数有两种形式:DataView()和DataView(DataTable)。第二种形式的参数为视图的来源DataTable对象。
2)示例
下列代码的作用是从学生数据表中筛选出所有成绩大于60的学生信息构成数据视图,并依次输出数据视图中所有学生的学号信息到页面上,每个学号信息用换行符分割。
筛选表达式的格式类似于SQL查询语句的Where子句。
(2)数据库操作公共工具类
从前面的例子可以看到,ADO.NET的各主要对象访问数据库的组合编程方式很固定。例如,每次都要编写创建连接对象,使用连接对象的Open和Close方法的代码;几乎每次都要编程创建命令对象的代码等,导致代码的冗余度很高。为了解决这个问题,我们可以创建一个公共工具类,将ADO.NET对数据库的增、删、改、查各种操作封装成工具类中的静态成员方法。这样,当对数据库进行访问时,就可以直接通过类名调用这些静态方法,而无需重复编写大量冗余的代码。
在ASP.NET中,公共类的代码文件必须创建在系统指定的目录下,才能被其他页面类所访问,具体过程如下:在解决方案资源管理器下的对应项目上右键单击,选择“添加ASP.NET文件夹”菜单,在弹出子菜单中点选“App_Code”。这时,就能看到在项目中新创建了一个名为“App_Code”的空文件夹,在该文件夹上右键单击,选择“添加新项”,在弹出的对话框的“模板”列表中选择“类”,将文件名称修改为“DbHelper.cs”,单击添加按钮关闭对话框后,就会发现在“App_Code”文件夹下已新建一个名为DbHelper.cs的公共类代码文件。
在该公共类文件中添加以下代码,使其成为一个封装了数据库操作的公共工具类。
该公共工具类一共提供了5个静态方法,分别实现了单值查询、单列查询、批量查询、单个增删改操作、一次性反向操作5种功能。在该公共工具类中,将连接对象定义为静态数据成员,这样做的好处是可以保证整个应用系统中将一直存在而且只有一个连接对象,否则,如果像以前那样将连接对象定义为局部对象,就会对其进行频繁的创建和销毁。
有了此工具类,就能有效地减少冗余代码,进行代码复用。例如将本任务第2步的批量读取图书信息的代码进行如下改写:
可以看出,只需一条语句就能实现同样的功能。而且,代码中没有出现任何的ADO.NET的对象,也就是说,即使不清楚ADO.NET的程序员也能使用该工具类,这样就增加了代码的通用性。
【教学评价】
评价标准见表3.2。
表3.2任务3.2评价标准
【归纳总结】
①ADO.NET是.NET framework提供的一组对象,它为应用程序提供访问数据库的接口,以实现应用程序对数据库检索和更新。ADO.NET由.NET数据提供程序和.DataSet数据集两个主要组件构成,其中,.NET数据提供程序所属对象针对异构数据库系统提供了不同的形式,但使用方法相同。ADO.NET各主要对象之间的相互协作关系如图3.12所示。
图3.12 ADO.NET各对象协作关系图
②DataSet对象是ADO.NET的一个重要组件,它是一种非连接的数据结构,可以看作是一个小型的内存数据库系统,与关系数据库类似,DataSet内部也具有数据表、数据列、数据行、约束、表间关系等内部元素和联系,DataSet只有唯一的形式的,它与不同形式的.NET数据提供程序的对象相结合,为异构的数据源提供了统一的编程模型。
③Ado.NET的编程模式比较固定,灵活性主要体现在SQL命令的编写上。因此,对于小型软件项目,一般可以将对数据库的增删改查操作做成静态方法,然后通过一个数据库辅助操作类来封装;对于更大型的项目,一般可以将数据库访问操作封装为软件项目分层体系结构中的一个独立层,称为数据访问层。关于软件系统尤其是Web应用系统的分层体系结构的内容,读者可自行上网学习。
【练习与实训】
实训1分以下步骤实现用户信息管理模块。
①创建数据库UserModule,创建用户表UserInfo,包括3个字段,分别为用户名(字符串)、密码(字符串)、权限(字符串,“管理员”和“一般用户”)。
②创建3个页面:用户登录页面、一般用户后台管理页面,管理员后台管理页面。
(用户登录后,根据不同权限,登录到一般用户后台管理页面或管理员后台管理界面)。
③在一般用户后台管理页面实现修改密码的功能,提供下拉框显示所有的用户名,选定后修改密码。
④在管理员后台管理页面实现用户删除的功能,提供下拉框显示所有的用户名,选择后进行删除。
⑤完成以上步骤后,复制1份,用Access数据库实现相同功能。
(连接字符串:“Provider=Microsoft.Jet.OLEDB.4.0;Data Source= access数据库文件路径名”)
实训2分以下步骤实现商品信息管理模块。
①创建数据库和商品信息表,字段包括商品编号、商品名称、商品类型、商品价格、商品产地、商品图片,自行命名。
②创建商品信息注册页面,实现商品信息注册功能。
③创建商品信息管理页面,实现商品信息的修改和删除功能,用下拉框显示所有的商品编号,选定后进行修改和删除,可以修改的字段包括:商品名称、商品价格、商品产地。
④完成以上步骤后,复制一份,用Access数据库实现相同功能。
实训3编程创建商品信息(商品名称、生产厂家、价格)和用户信息(用户名、性别、年龄、出生地)的DataTable数据表,编程为两个DataTable添加若干条记录,将两个数据表加入数据集DataSet中,直接用GridView控件将两个内存表显示出来,用Label控件显示两个表格中的第2行第2列的值。
实训4创建用户信息数据表(用户名、性别、年龄、出生地),输入若干记录,使用Connection对象、DataAdapter对象和DataSet对象,以及GridView控件将用户信息表显示在页面上;直接编程实现对内存中的用户信息数据表进行新增、修改和删除,使用DataAdapter对象的Update方法将修改后的用户信息数据表提交回数据库。