第10章 使用ADO.NET访问数据库
ADO.NET是美国微软公司推出的、由ADO(Microsoft ActiveX Data Objects)演变而来的数据访问技术。作为.NET框架的一部分,ADO.NET绝不仅仅是前一版本ADO的简单升级。ADO.NET提供了一组功能强大的.NET类,这些类不仅有助于实现对各种数据源进行高效访问,使用户能够对数据进行复杂的操作,而且形成了一个重要的框架,在这个框架中可以实现应用程序之间的通信和XMLWeb服务。
10.1 ADO.NET概述
ADO.NET是对ADO的一个跨时代的改进,它们之间有很大的差别。主要表现在ADO.NET可通过DateSet对象在“断开连接模式”下访问数据库,即用户访问数据库中的数据时,首先要建立与数据库的连接,从数据库中下载需要的数据到本地缓冲区,之后断开与数据库的连接。此时用户对数据的操作(如查询、添加、修改和删除等)都是在本地进行的,只有需要更新数据库中的数据时才再次与数据库连接,在发送修改后的数据到数据库后关闭连接。这样大大减少了因连接过多(访问量较大时)而导致对数据库服务器资源大量占用的问题。
ADO.NET也支持在连接模式下的数据访问方法,该方法主要通过DataReader对象实现。该对象表示一个向前的、只读的数据集合,其访问速度非常快,效率极高,但其功能有限。
此外,由于ADO.NET传送的数据都是XML格式的,因此任何能够读取XML格式的应用程序都可以使用ADO.NET进行数据处理。事实上,接收数据的组件不一定必须是ADO.NET组件,它可以是一个基于Microsoft Visual Studio的解决方案,也可以是任何运行在其他平台上的任何应用程序。
10.1.1 ADO.NET的数据模型
ASP.NET使用ADO.NET数据模型来实现对数据库的连接和各种操作。ADO.NET数据模型由ADO发展而来,其特点主要有以下几个。
1)ADO.NET不再采用传统的ActiveX技术,是一种与.NET框架紧密结合的产物。
2)ADO.NET包含对XML标准的全面支持,这对于实现跨平台的数据交换具有十分重要的意义。
3)ADO.NET既能在数据源连接的环境下工作,也能在断开数据源连接的条件下工作。特别是后者,非常适合网络环境中多用户应用的需要。因为在网络环境中若持续保持与数据源的连接,不但效率低下而且占用的系统资源也很大,经常会因多个用户同时访问同一资源而造成冲突。ADO.NET较好地解决了在断开网络连接的情况下正确进行数据处理的问题。
应用程序和数据库之间保持连续的通信,称为“已连接环境”。这种方法能及时刷新数据库,安全性较高。但是,由于需要保持持续的连接,所以需要固定的数据库连接,如果使用在Internet上,则对网络的要求较高,并且不宜多个用户共同使用同一个数据库,所以扩展性差。一般情况下,数据库应用程序使用该类型的数据连接。
随着网络的发展,许多应用程序要求能在与数据库断开的情况下进行操作,出现了非连接环境。这种环境中,应用程序可以随时连接到数据库获取相应的信息。但是,由于与数据库的连接是间断的,可能获得的数据不是最新的,并且对数据进行更改时可能引发冲突,因为在某一时刻可能有多个用户同时对同一数据进行操作。
ADO.NET采用了层次管理的结构模型,各部分之间的逻辑关系如图10-1所示。结构的最顶层是应用程序(ASP.NET网站或Win-dows应用程序),中间是数据层(ADO.NET)和数据提供器(Provider),在这个层次中数据提供器起到了关键作用。
数据提供器(也称为“数据提供程序”)相当于ADO.NET的通用接口,各种不同类型的数据源需要使用不同的数据提供器。它相当于一个容器,包括一组类及相关的命令,它是数据源(DataSource)与数据集(Data-Set)之间的桥梁,负责将数据源中的数据读入到数据集中(内存中),也可将用户处理完毕的数据集保存到数据源中。
图10-1 ADO.NET的层次结构
10.1.2 ADO.NET中的常用对象
前面介绍过的数据源控件及各类数据显示控件,可以方便地、几乎无须编写任何代码完成对数据库的一般操作。但这种方式下若希望修改各类控件的外观设计或希望执行特殊的数据库操作时,就有显得有些困难了。
在ASP.NET中,除了可以使用控件完成数据库信息的浏览和操作外,还可以使用ADO.NET提供的各种对象,通过编写代码自由地实现更复杂、更灵活的数据库操作功能。
ADO.NET对象主要指包含在数据集(DataSet)和数据提供器(Provider)中的对象。使用这些对象可通过代码自由地创建符合用户需求的数据库Web应用程序。
在ADO.NET中,数据集(DataSet)与数据提供器(Provider)是两个非常重要而又相互关联的核心组件。它们两者之间的关系如图10-2所示。
DataSet对象用于以数据表形式在程序中放置一组数据,它不关心数据的来源。DataSet是实现ADO.NET断开式连接的核心,应用程序从数据源读取的数据暂时被存放在DataSet中,程序再对其中的数据进行各种操作。
Provider中包含许多针对数据源的组件,开发人员通过这些组件可以使程序与指定的数据源进行连接。Provider主要包括Connection对象、Command对象、DataReader对象和DataAdapter对象。Provider用于建立数据源与数据集之间的连接,它能连接各种类型的数据源,并能按要求将数据源中的数据提供给数据集,或者将应用程序编辑后的数据发送回数据库。
图10-2 数据集与数据提供器的关系
10.2 数据库连接对象(Connection)
Connection类提供了对数据源连接的封装。类中包括连接方法及描述当前连接状态的属性。Connection类中最重要的属性是ConnectionString(连接字符串),该属性用来指定服务器名称、数据源信息及其他登录信息。
Connection对象的功能是创建与指定数据源的连接,并完成初始化工作。它提供了一些属性用来描述数据源和进行用户身份验证。Connection对象还提供一些方法允许程序员与数据源建立连接或者断开连接。
对不同的数据源的类型,使用的Connection对象也不同,ADO.NET中提供了以下4种数据库连接对象来连接到不同类型的数据源。
1)要连接到Microsoft SQL Server 7.0或更高版本,应使用SqlConnection对象。
2)要连接到OLE DB数据源、Microsoft SQL Server 6.x或更低版本、Access或Excel,应使用OleDbConnection对象。
3)要连接到ODBC数据源,应使用OdbcConnection对象。
10.2.1 创建Connection对象
使用Connection对象的构造函数创建SqlCommand对象,并通过构造函数的参数来设置Connection对象的特定属性值的语法格式如下。
SqlConnection连接对象名=newSqlConnection(连接字符串);
也可以首先使用构造函数创建一个不含参数的Connection对象实例,然后再通过连接对象的ConnectionString属性,设置连接字符串。其语法格式如下。
SqlConnection连接对象名=newSqlConnection();
连接对象名.ConnectionString=连接字符串;
以上两种方法在功能上是等效的。选择哪种方法取决于个人喜好和编码风格。不过,对属性进行明确设置确实能够使代码更易理解和调试。
创建其他类型的Connection对象时,仅需将上述语法格式中的SqlConnection替换成相应的类型即可。例如,下列语法格式用于创建一个用于连接Access数据库的Connection对象。
OleDbConnection连接对象名=newOleDbConnection();
连接对象名.ConnectionString=连接字符串;
10.2.2 数据库的连接字符串
为了连接到数据源,需要使用一个提供数据库服务器的位置、要使用的特定数据库及身份验证等信息的连接字符串,它由一组用分号“;”隔开的“参数=值”组成。
连接字符串中的关键字不区分大小写。但根据数据源不同,某些属性值可能是区分大小写的。此外,连接字符串中任何包含分号、单引号或双引号的值都必须用双引号括起来。Connection对象的连接字符串保存在ConnectionString属性中,可以使用ConnectionString属性来获取或设置数据库的连接字符串。
1.连接字符串中的常用属性
表10-1列出了数据库连接字符串常用的属性及说明。
表10-1 数据库连接字符串中常用的属性及说明
2.连接到SQL Server的连接字符串
SQL Server的.NET Framework数据提供程序通过SqlConnection对象的ConnectionString属性,设置或获取连接字符串,可以连接Microsoft SQL Server 7.0或更高版本。
有两种连接数据库的方式:标准安全连接和信任连接。
(1)标准安全连接
标准安全连接(Standard Security Connection)也称为非信任连接。它把登录账户(User ID或Uid)和密码(Password或Pwd)写在连接字符串中。
其语法格式如下。
"DataSource=服务器名或IP;Initial Catalog=数据库名;User ID=用户名;Password=密码"
或者
"Server=服务器名或IP;Database=数据库名;Uid=用户名;Pwd=密码;Trusted_Connection=False"
如果要连接到本地的SQL Server服务器,可使用localhost作为服务器名称。
(2)信任连接
信任连接(Trusted Connection)也称为“SQL Server集成安全性”,这种连接方式有助于在连接到SQL Server时提供安全保护,因为它不会在连接字符串中公开用户ID和密码,这是安全级别要求较高时推荐的数据库连接方法。对于集成Windows安全性的账号而言,其连接字符串的形式一般如下。
"Data Source=服务器名或IP地址;Initial Catalog=数据库名;Integrated Security=SSPI"
或者
"Server=服务器名或IP地址;Database=数据库名;Trusted_Connection=True"
3.连接到OLE DB数据源的连接字符串
OLEDB的.NET Framework数据提供器通过OleDbConnection对象的ConnectionString属性,设置或获取连接字符串,提供与OLE DB公开数据源的连接或SQL Server 6.x更早版本的连接。
对于OLE DB.NET Framework数据提供程序,连接字符串格式中的Provider关键字是必需的,必须为OleDbConnection连接字符串指定提供程序名称。下列连接字符串使用Jet提供程序连接到一个Microsoft Access 2003数据库(.mdb)。
"Provider=Microsoft.Jet.OLEDB.4.0;Data Source=数据库名;User ID=用户名;Password=密码"
下列代码为连接到Microsoft Access 2007/2010数据库(.accdb,无访问密码)的连接字符串。
"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=|DataDirectory|\test.accdb"
其中,“|DataDirectory|”表示网站的App_Data文件夹。
4.连接字符串的存放位置
连接字符串可以书写在程序代码中,也可以书写在网站的配置文件web.config中。在.NET Framework 2.0以上版本中,ConfigurationManager类新增了一个connectionStrings属性,专门用来获取web.config配置文件中<configuration>元素的<connectionStrings>节的数据。<connectionStrings>中有3个重要部分:字符串名、字符串的内容和数据提供器名称。
下面的web.config配置文件片段说明了用于存储连接字符串的架构和语法。在<config-uration>元素中,创建一个名为<connectionStrings>的子元素并将连接字符串置于其中,语法格式如下。
子元素add用来添加属性。add有3个属性:name、connectionString和providerName。
1)name属性是唯一标识连接字符串的名称,以便在程序中检索到该字符串。
2)connectionString属性是描述数据库的连接字符串。
3)providerName属性是描述.NET Framework数据提供程序的固定名称,其名称为Sys-tem.Data.SqlClient(默认值)、System.Data.OldDb或System.Data.Odbc。
应用程序中任何页面上的任何数据源控件都可以引用此连接字符串项。将连接字符串信息存储在web.config文件中的优点是,程序员可以方便地更改服务器名称、数据库或身份验证信息,而无须逐个修改程序。
在程序中获得<connectionStrings>连接字符串的语法格式如下。
System.Configuration.ConfigurationManager.ConnectionStrings["连接字符串名"].ToString();
如果在程序中引入ConfigurationManager类的命名空间“usingSystem.Configuration;”,则在程序中获得<connectionStrings>连接字符串的方法可简写为下列格式。
ConfigurationManager.ConnectionStrings["连接字符串名"].ToString();
打开和修改web.config的方法如下。
1)在解决方案资源管理器中双击web.config文件名。
2)在打开的文件中找到<configuration>元素中的<connectionStrings/>子元素,删除<connectionStrings/>的后两个字符“/>”,成为“<connectionStrings”,然后输入“>”,这时系统将自动填充</connectionStrings>。在<connectionStrings>与</connectionStrings>之间输入如下所示的配置数据。
上述配置创建了一个名为StudentDBConnectionString的、连接到名为vm2k8s的SQL Server服务器的、SQL Server用户名为sa密码为123456的连接字符串。
又如,下列代码创建了一个与SQL Server LocalDB数据库文件mydb.mdf连接的连接字符串“ConnStr”。
10.3 数据库命令对象(Command)
Command对象用于在数据源上执行的SQL语句或存储过程,该对象最常用的属性是CommandText,用于设置针对数据源执行的SQL语句或存储过程。
连接好数据源后,就可以对数据源执行一些命令操作。命令操作包括对数据的查询、插入、更新、删除和统计等。在ADO.NET中,对数据库的命令操作是通过Command对象来实现的。从本质上讲,ADO.NET的Command对象就是SQL命令或者是对存储过程的引用。除了查询或更新数据命令之外,Command对象还可用来对数据源执行一些不返回结果集的查询命令,以及用来执行改变数据源结构的数据定义命令。
根据所用的数据源类型不同,Command对象也分为4种,分别是OleDbCommand对象、SqlCommand对象、OdbcCommand对象和OracleCommand对象。
10.3.1 创建Command对象
使用Connection对象与数据源建立连接后,可使用Command对象对数据源执行各种操作命令并从数据源中返回结果。可以使用对象来创建Command对象。可通过对象的构造函数或调用CreateCommand()方法来创建Command对象。
1.使用构造函数创建Command对象
使用构造函数创建SqlCommand对象的语法格式如下。
SqlCommand命令对象名=newSqlCommand(查询字符串,连接对象名);
举例如下。
SqlCommand cmd=new SqlCommand("SELECT∗FROM StudentInfo",conn);
也可以先使用构造函数创建一个空Command对象,然后直接设置各属性值。这种写法能够使代码更易理解和调试。其语法格式如下。
SqlCommand命令对象名=newSqlCommand();
命令对象名.Connection=连接对象名;
命令对象名.CommandText=查询字符串;
例如,下面的代码在功能上与前面介绍的方法是等效的。
sqlCommand cmd=new SqlCommand();
cmd.Connection=conn; //conn是前面创建的连接对象名
cmd.CommandText="SELECT∗FROM StudentInfo";
2.使用CreateCommand()方法创建Command对象
使用Connection对象的CreateCommand()方法也可以创建Command对象。由Command对象执行的SQL语句可以使用CommandText属性进行配置。
下面是使用Connection对象的CreateCommand()方法创建SqlCommand对象的语法格式。
SqlCommand命令对象名=连接对象名.CreateCommand();
命令对象名.CommandText=查询字符串;
例如,通过Command对象的CommandText属性来执行一条SQL语句的代码如下。
//从web.config中获取连接字符串
string ConnStr=ConfigurationManager.ConnectionStrings["StudentDBConnectionString"].ToString();
SqlConnection conn=new SqlConnection(ConnStr);//创建数据库连接对象conn
string sqlstr="SELECT∗FROM StudentInfo";
//创建Command对象,并初始化查询字符串
SqlCommand cmd=new SqlCommand(sqlstr,conn);
如果要通过Command对象的CommandText属性来执行存储过程ProcName,代码可按以下方式书写。
SqlCommand cmd=new SqlCommand("ProcName",conn);
cmd.CommandType=CommandType.StoredProcedure; //调用存储过程或者
SqlCommand command=new SqlCommand(); //创建Command对象
command.Connection=connection; //设置Connection属性
command.CommandType=CommandType.StoredProcedure; //设置为存储过程
command.CommandText="ProcName"; //设置存储过程的名称需要注意的是,使用CommandType属性需要引用命名空间“usingSystem.Data;”。
10.3.2 Command对象的属性和方法
Command对象的常用属性如表10-2所示。
表10-2 Command对象常用的属性及说明
Command对象的方法统称为Execute方法,常用方法及说明如表10-3所示。
表10-3 Command对象的常用方法及说明
1.ExecuteScalar()方法
如果需要返回的只是单个值的数据库信息,而不需要返回表或数据流形式的数据库信息,则可使用该方法。例如,可能需要返回COUNT()、SUM()或AVG()等聚合函数的结果,以及INSERT、UPDATE、DELETE和SELECT受影响的行数。这时就要使用Command对象的ExecuteScalar方法,返回一个标量值。如果在一个常规查询语句中调用该方法,则只读取第一行第一列的值,而丢弃所有其他值。其语法格式如下。
命令对象名.ExecuteScalar();
使用ExecuteScalar()方法时,首先需要创建一个Command对象,然后使用ExecuteScalar()方法执行该对象设置的SQL语句。
【演练10-1】使用SqlCommand对象的ExecuteScalar()方法返回表中的记录总数。
程序设计步骤如下。
新建一个ASP.NET空网站,向网站中添加一个Web窗体Default.aspx和用于存放数据库文件的App_Data文件夹。将第9章中创建的包含有SQL Server的数据库文件employee.mdf和employee_log.ldf复制到App_Data中(复制后要刷新文件夹)。
在解决方案资源管理器中双击打开web.config文件,参照前面介绍的方法向其中添加连接字符串。
切换到Default.aspx的代码窗口编写程序代码。
在命名空间区域中添加下列引用。
using System.Data;
using System.Data.SqlClient;
using System.Configuration;
Default.aspx页面载入时执行的事件代码如下。
2.ExecuteNonQuery()方法
使用Command对象的ExecuteNonQuery()方法,可以方便地处理那些修改数据但不返回行的SQL语句(如Insert、Update和Delete等),以及用于修改数据库或编录架构的语句。(如CreateTable、AlterColumn等)。
使用ExecuteNonQuery()方法执行更新操作时将返回一个整数,表示受影响的记录数。如果执行了多条语句,则返回的值为受影响的记录总数。
ExecuteNonQuery()方法的语法格式如下。
命令对象名.ExecuteNonQuery();
例如,下列代码使用ExecuteNonQuery()方法执行一条SQL语句,将一条新记录插入到数据表中,同时在页面中显示受影响的记录数。
如果插入记录后在“服务器资源管理管理器”中看到新记录中的中文字段值变成了“?”,则需通过Windows“开始”菜单启动“Microsoft SQL Server Management Studio”,使用“Windows身份验证”方式连接数据库实例“(locadb)\MSSQLLocalDB”,如图10-3所示。右击数据库名称,在弹出的快捷菜单中选择“属性”命令,在打开的如图10-4所示的对话框的“选项页”窗格中选择“选项”,在右侧窗格中修改“排序规则”为Chinese_PRC_CS_AI_WS。
图10-3 连接到服务器
图10-4 修改排序规则
10.4 ExecuteReader()方法和DataReader对象
通过ExecuteReader()方法执行CommandText中定义的SQL语句或存储过程,可以返回一个DataReader(数据阅读器)对象。该对象是包含了一行或多行数据记录的结果集。使用DataReader对象提供的方法可以实现对结果集中数据的检索数据。
DataReader对象拥有以下一些特点。
1)DataReader对象是一种只读的、只能向前移动的游标。
2)DataReader每次只能在内存中保留一行,所以开销非常小。
3)DataReader对象的工作过程中需要一直保持与数据库的连接,不能提供非连接的数据访问。
4)使用OLEDB数据库编程时需要使用OleDbDataReader对象,使用SQL Server数据库编程时,则应使用SqlDataReader对象。
10.4.1 使用ExecuteReader()方法创建DataReader对象
ExecuteReader()方法的语法格式如下。
SqlDataReader对象名=命令对象名.ExecuteReader();
或
OleDbDataReader对象名=命令对象名.ExecuteReader();
其中,“对象名”是创建的DataReader对象的名称,“命令对象名”是Command对象的名称。使用ExecuteReader()方法时,首先需要创建一个Command对象,然后使用Exe-cuteReader()方法创建DataReader对象来对数据源进行读取。
10.4.2 DataReader对象的常用属性及方法
OleDbDataReader或SqlDataReader对象常用的属性和方法有以下几个。
1)FieldCount属性:该属性用来获取当前行中的列数,如果未放置在有效的记录集中,则返回0,否则返回列数(字段数),默认值为-1。
2)HasRows属性:该属性用来获取DataReader对象中是否包含任何行。
3)Read()方法:使用该方法可将Reader指向当前记录,并将记录指针移到下一行,从而可使用列名或列的次序来访问列的值。如果已到了数据表的最后,则返回一个布尔值false。
4)GetValue()方法:获取以本机格式表示的指定列的值。
5)Close()方法:该方法用来关闭DataReader对象,并释放对记录集的引用。
【演练10-2】使用ExecuteReader()方法和DataReader对象设计一个通过员工姓名模糊查询记录的应用程序。数据库仍使用前面创建的employee数据库中的emp表,要求通过数据库中的存储过程GetData实现程序功能。程序运行结果如图10-5所示。
图10-5 程序运行结果
程序设计步骤如下。
(1)设计Web界面及控件属性
新建一个ASP.NET空网站,向网站中添加一个Web窗体页Default.aspx,向页面中添加一个文本框控件TextBox1、一个按钮控件Button1和一个数据表控件GridView1。
设置TextBox1的ID属性为txtKey;设置Button1的ID属性为btnQuery,Text属性为“查询”。
在网站中添加一个用于存放数据库文件的App_Data文件夹,将数据库文件employ-ee.mdf和日志文件employee_log.ldf复制到该文件夹中并刷新文件夹。
(2)在web.config文件中配置连接字符串
在解决方案资源管理器中双击打开web.config文件,添加如下所示的连接字符串代码。
(3)创建存储过程
在“服务器资源管理器”窗口中右击employee数据库下的“存储过程”选项,在弹出的快捷菜单中选择“添加新存储过程”命令,在打开的窗口中输入如下所示的代码,然后单击“更新”。
(4)编写程序代码
切换到Default.aspx的代码编辑窗口,添加必需的命名空间引用。
usingSystem.Data;
usingSystem.Data.SqlClient;
usingSystem.Configuration;
编写“查询”按钮被单击时执行的事件处理代码如下。
思考:如果用户没有输入任何查询关键字直接单击“查询”按钮,将得到怎样的结果?为什么?
【演练10-3】使用DataReader对象设计一个用户登录身份验证页面,页面打开时如图10-6所示,用户在输入了正确的用户名和密码后,程序将根据用户级别跳转到不同的页面。
图10-6 成功登录后根据用户级别不同跳转到不同页面
设已完成了Access数据库manager.mdb的设计,并在其中创建了如图10-7所示的用于存放用户信息的Admin表。表中的uname字段表示用户名,upwd字段表示密码,ulevel字段表示用户级别,0表示管理员,1表示普通用户(游客)。
程序设计步骤如下。
(1)设计Web页面
图10-7 用户信息
新建一个ASP.NET空网站,向网站中添加一个Web窗体页Default.aspx,向页面中添加一个用于布局的HTML表格,适当调整表格的行列数。向表格中添加必要的说明文字,添加两个文本框控件TextBox1和TextBox2,添加一个按钮控件Button1。适当调整各控件的大小及位置,适当调整HTML表格边框和文本框边框的样式。将manager.mdb数据库文件复制到站点App_Data文件夹中。
向网站中添加两个新网页Admin.aspx和Guest.aspx,并分别写入文本“这是管理员页面”和“这是游客页面”。
(2)设置对象属性
设置两个文本框的ID属性分别为txtUserName和txtPassword;设置“密码”文本框的TextMode属性为password;设置按钮Button1的ID属性为btnOK,Text属性为“登录”,控件的其他初始属性将在页面载入事件中通过代码进行设置。
(3)编写代码
1)在解决方案资源管理器中双击打开web.config文件,添加如下所示的连接字符串配置代码。
2)在代码窗口最上方的命名空间引用区中引入需要的命名空间。
usingSystem.Data;
usingSystem.Configuration;
usingSystem.Data.OleDb;
3)Default.aspx载入时执行的事件处理代码如下。
4)“登录”按钮被单击时执行的事件过程代码如下。
5)页面Admin.aspx载入时执行的事件过程代码如下。
6)Guest.aspx页面载入时执行的事件代码如下。
10.5 数据适配器对象(DataAdapter)
DataAdapter对象在物理数据库表和内存数据表(结果集)之间起着桥梁作用。它通常需要与DataTable对象或DataSet对象配合来实现对数据库的操作。
10.5.1 DataAdapter对象概述
DataAdapter对象是一个双向通道,用来把数据从数据源中读到一个内存表中或把内存中的数据写回到一个数据源中。这两种情况下使用的数据源可能相同,也可能不相同。通常将把数据源中的数据读取到内存的操作称为填充(Fill),将把内存中的数据写回数据库的操作称为更新(Update)。DataAdapter对象通过Fill方法和Update方法来提供这一桥接通道。
DataAdapter对象可以使用Connection对象连接到数据源,并使用Command对象从数据源检索数据,以及将更改写回数据源。
如果所连接的是SQL Server数据库,需要将SqlDataAdapter与关联的SqlCommand和Sql-Connection对象一起使用。
如果连接的是Access数据库或其他类型的数据库,则需要使用OleDbDataAdapter、Odb-cDataAdapter或OracleDataAdapter对象。
10.5.2 DataAdapter对象的属性和方法
与其他所有对象一样,DataAdapter对象在使用前也需要进行实例化。下面以创建Sql-DataAdapter对象为例,介绍使用DataAdapter类的构造函数创建DataAdapter对象的方法。
常用的创建SqlDataAdapter对象的语法格式如下。
SqlDataAdapter对象名=newSqlDataAdapter(SqlStr,conn);
其中,SqlStr为Select查询语句或SqlCommand对象,conn为SqlConnection对象。
1.DataAdapter对象的常用属性
DataAdapter对象的常用属性如表10-4所示。
表10-4 DataAdapter对象的常用属性及说明
需要注意的是,DataAdapter对象的SelectCommand、InsertCommand、UpdateCommand和DeleteCommand属性都是Command类型的对象。
设已创建了用于删除数据表记录的SQL语句StrDel,并且已建立了与Access数据库的连接对象conn,则下列代码说明了如何在程序中通过DataAdapter对象的DeleteCommand属性删除记录的程序设计方法。
OleDbCommand DelCom=new OleDbCommand(StrDel,conn); //创建Command对象
OleDbDataAdapter da=new OleDbDataAdapter(); //创建DataAdapter对象
conn.Open();
da.DeleteCommand=DelCom; //设置DataAdapter对象的DeleteCommand属性
da.DeleteCommand.ExecuteNonQuery(); //执行DeleteCommand代表的SQL语句(删除记录)
conn.Close();
2.DataAdapter对象的常用方法
DataAdapter对象的常用方法如表10-5所示。
表10-5 DataAdapter对象的常用方法及说明
10.5.3 DataTable对象
DataTable对象是内存中的一个关系数据库表,可以独立创建也可以由DataAdapter来填充。声明一个DataTable对象的语法格式如下。
DataTable对象名=newDataTable();
一个DataTable对象被创建后,通常需要调用DataAdapter的Fill()方法对其进行填充,使DataTable对象获得具体的数据集,而不再是一个空表对象。
1.创建DataTable对象
在实际应用中使用DataTable对象一般需要经过以下几个步骤。
1)创建数据库连接。
2)创建Select查询语句或Command对象。
3)创建DataAdapter对象。
4)创建DataTable对象。
5)调用DataAdapter对象的Fill()方法填充DataTable对象。
需要注意的是,使用DataTable对象需要引用System.Data命名空间。
【演练10-4】按照上述步骤创建并填充DataTable对象的示例,程序最终将DataTable对象作为GridView控件的数据源,将数据显示到页面中。程序运行结果如图10-8所示。
程序设计步骤如下。
1)新建一个ASP.NET空网站,向页面中添加一个用于显示数据的GridView控件。将事先准备好的Access数据库student.mdb复制到网站的App_Data文件夹下。
图10-8 程序运行结果
2)切换到Default.aspx的代码编辑窗口,在命名空间引用区中添加必需的引用。
using System.Data.OleDb;
using System.Data;
3)Default.aspx页面载入时执行的事件代码如下。
2.DataTable对象的常用属性
DataTable对象的常用属性主要有Columns、Rows和DefaultView。
1)Columns属性:用于获取DataTable对象中表的列集合。
2)Rows属性:用于获取DataTable对象中表的行集合。
3)DefaultView属性:用于获取可能包括筛选视图或游标位置的表的自定义视图。
下列代码说明了使用DataAdapter、DataTable和DataRow对象配合实现修改数据记录的程序设计方法。
… //声明查询字符串SqlStr,并创建OleDbConnection对象conn
OleDbDataAdapter da=new OleDbDataAdapter(SqlStr,conn);
DataTable dt=newDataTable();
//为DataAdapter自动生成更新命令
OleDbCommandBuilder builder=new OleDbCommandBuilder(da);
da.Fill(dt);
DataRow myrow=dt.Rows[0]; //声明一个行对象myrow,并将dt的第1行数据存入对象中
myrow[2]="女"; //修改dt对象中第1行第3列的字段值为“女”
da.Update(dt); //调用DataAdapter对象的Update()方法,将修改提交到数据库
下列代码说明了使用DataAdapter、DataTable和DataRow对象配合实现添加新数据记录的程序设计方法。
…//声明查询字符串SqlStr,并创建OleDbConnection对象conn
OleDbDataAdapter da=new OleDbDataAdapter(SqlStr,conn);
DataTable dt=new DataTable();
//为DataAdapter自动生成更新命令
OleDbCommandBuilderbuilder=new OleDbCommandBuilder(da);
da.Fill(dt);
//创建一个DataRow对象,并为其赋值为dt对象的新行
DataRow myrow=dt.NewRow();
myrow[0]="200909"; //为新行的各字段赋值
myrow[1]="zhangsan";
myrow[2]="男";
…
dt.Rows.Add(myrow); //将赋值完成的各字段组成的新行添加到dt对象中
da.Update(dt); //调用DataAdapter对象的Update()方法,将修改提交到数据库
需要说明的是,当使用“OleDbCommandBuilder builder=new OleDbCommandBuilder(da);”语句为DataAdapter对象自动生成更新命令时,要求数据库表中必须设有主键。
10.6 DataSet概述
DataSet(数据集)对象是ADO.NET的核心构件之一,它是数据的内存流表示形式,提供了独立于数据源的关系编程模型。DataSet表示整个数据集,其中包括表、约束,以及表与表之间的关系。由于DataSet独立于数据源,故其中既可以包含应用程序的本地数据,也可以包含来自多个数据源的数据。这是DataSet与前面介绍的DataTable的关键不同点。
DataSet提供了对数据库的断开操作模式(也称为离线操作模式),当DataSet从数据源获取数据后就断开了与数据源之间的连接。在本地完成了各项数据操作(增、删、改、查等)后,可以将DataSet中的数据送回到数据源以更新数据库记录。
10.6.1 DataSet与DataAdapter的关系
DataSet是实现ADO.NET断开式连接的核心,它通过DataAdapter从数据源获得数据后就断开了与数据源之间的连接(这一点与前面介绍过的DataReader对象完全不同),此后应用程序对数据源的所有操作(如定义约束和关系、添加、删除、修改、查询、排序、统计等)均转向到DataSet,当所有这些操作完成后可以通过DataAdapter提供的数据源更新方法将修改后的数据写入数据库。
图10-9表示了DataSet、DataAdapter和数据源之间的关系,从图中可以看到DataSet对象并没有直接连接数据源,它与数据源之间的连接是通过DataAdapter对象来完成的。
图10-9 DataSet、DataAdapter和数据源之间的关系
需要说明的是,对于不同的数据源DataAdapter对象也有不同的形式,如用于连接Ac-cess数据库的OleDbDataAdapter,用于连接SQL Server数据库的SQL DataAdapter,用于连接ODBC数据源的OdbcDataAdapter,以及用于连接Oracle数据库的OracleDataAdapter等。
10.6.2 DataSet的组成
DataSet主要由DataRelationCollection(数据关系集合)、DataTableCollection(数据表集合)和ExtendedProperties对象组成,如图10-10所示。其中最基本也是最常用的是DataT-ableCollection。
图10-10 DataSet组成结构简图
1.DataRelationCollection
DataRelationCollection对象用于表示DataSet中两个DataTable对象之间的父子关系,它使一个DataTable中的行与另一个DataTable中的行相关联,这种关联类似于关系数据库中数据表之间的主键列和外键列之间的关联。DataRelationCollection对象管理DataSet中所有的DataTable之间的DataRelation关系。
2.DataTableCollection
在每一个DataSet对象中可以包含由DataTable(数据表)对象表示的若干个数据表的集合。而DataTableCollection对象则包含了DataSet对象中的所有DataTable对象。
DataTable在System.Data命名空间中定义,表示内存驻留数据的单个表。其中包含由DataColumnCollection(数据列集合)表示的数据列集合和由ConstraintCollection表示的约束集合,这两个集合共同定义表的架构。隶属于DataColumnCollection对象的DataColumn(数据列)对象则表示了数据表中某一列的数据。
此外,DataTable对象还包含有DataRowCollection所表示的数据行集合,而DataRow(数据行)对象则表示数据表中某行的数据。除了反映当前数据状态之外,DataRow还会保留数据的当前版本和初始版本,以标识数据是否曾被修改。
隶属于DataTable对象的DataView(数据视图)对象用于创建存储在DataTable中的数据的不同视图。通过使用DataView,可以使用不同的排列顺序公开表中的数据,并且可以按行状态或基于过滤器表达式来过滤数据。
3.ExtendedProperties
ExtendedProperties对象其实是一个属性集合(PropertyCollection),用户可以在其中放入自定义的信息,如用于产生结果集的Select语句,或生成数据的时间/日期标志。
因为ExtendedProperties可以包含自定义信息,所以在其中可以存储额外的、用户定义的DataSet(DataTable或DataColumn)数据。
10.6.3 DataSet中的对象、属性和方法
DataSet内部是一个或多个DataTable的集合。每个DataTable由DataColumn、DataRow和Constraint(约束)的集合,以及DataRelation的集合组成。DataTable内部的DataRelation集合对应于父关系和子关系,两者建立了DataTable之间的连接。
1.DataSet中的对象
DataSet由大量相关的数据结构组成,其中最常用的有以下5个子对象,其名称及功能说明如表10-6所示。
表10-6 DataSet对常用子对象及说明
DataSet是数据的一种内存驻留表示形式,无论它包含的数据来自哪个数据源,都会提供一致的关系编程模型。DataSet表示整个数据集,其中包含对数据进行包含、排序和约束的表,以及表之间的关系。
2.DataSet对象的常用属性
DataSet对象的常用属性如表10-7所示。
表10-7 DataSet对象的常用属性及说明
3.DataSet对象的常用方法
DataSet对象的常用方法如表10-8所示。
表10-8 DataSet对象的常用方法及说明
使用DataSet的方法有若干种,这些方法可以单独应用,也可以结合应用。常用的应用形式有以下3种。
1)以编程方式在DataSet中创建DataTable、DataRelation和Constraint,并使用数据填充表。
2)通过DataAdapter用现有关系数据源中的数据表填充DataSet。
3)使用XML加载和保持DataSet内容。
10.7 使用DataSet访问数据库
DataSet的基本工作过程为:首先完成与数据库的连接,DataSet可在存放ASP.NET网站的服务器上为每个用户开辟一块内存,通过DataAdapter(数据适配器)将得到的数据填充到DataSet中,然后把DataSet中的数据发送给客户端。
ASP.NET网站服务器中的DataSet使用完以后,将释放DataSet所占用的内存。客户端读入数据后,在内存中保存一份DataSet的副本,随后断开与数据库的连接。
在这种方式下,应用程序所有针对数据库的操作都是指向DataSet的,并不会立即引起数据库的更新。待数据库操作完毕后,可通过DataSet和DataAdapter提供的方法将更新后的数据一次性保存到数据库中。
10.7.1 创建DataSet
创建数据集对象的语法格式如下。
DataSet数据集对象名=new DataSet();
或
DataSet数据集对象名=new DataSet("表名");
其中,前一个语法格式表示要先创建一个空数据集,以后再将已经建立的数据表(DataTable)包含进来;后一条语句是先建立数据表,然后建立包含该数据表的数据集。
10.7.2 填充DataSet
所谓“填充”,是指将DataAdapter对象通过执行SQL语句从数据源得到的返回结果,使用DataAdapter对象的Fill()方法传递给DataSet对象。
其常用语法格式如下。
Adapter.Fill(ds);
或
Adapter.Fill(ds,tablename);
其中,Adapter为DataSetAdapter对象实例;ds为DataSet对象;tablename为用于数据表映射的源表名称。在第一种格式中仅实现了DataSet对象的填充,而第二种格式则实现了填充DataSet对象和指定一个可以引用的别名两项任务。
需要说明的是,Fill()方法的重载方式(语法格式)有很多(共有13种),上面介绍的仅是最常用的两种,读者可查阅MSDN来了解其他重载方式。
DataSet对象支持多结果集的填充,也就是说可以将来自同一数据表或不同数据表中不同的数据集合同时填充到DataSet中。
例如,下列代码将来自同一数据表的不同数据集合(性别为“女”的所有记录和电子邮箱地址中包含“163”的所有记录)填充到了同一个DataSet对象中,然后通过DataSet对象的Tables属性分别将它们显示到两个不同的GridView控件中。
程序运行结果如图10-11所示。
图10-11 向DataSet中填充多个结果集
10.7.3 添加新记录
DataAdapter是DataSet与数据源之间的桥梁,它不但可以从数据源返回结果集并填充到DataSet中,还可以调用其Update()方法将应用程序对DataSet的修改(添加、删除或更新)回传到数据源,完成数据库记录的更新。
当调用Update()方法时,DataAdapter将分析已做出的更改,并执行相应的命令(如插入、更新或删除)。
DataAdapter的InsertCommand、UpdateCommand和DeleteCommand属性也是Command对象,用于按照DataSet中数据的修改来对数据源相应数据进行更新。
通过DataSet向数据表添加新记录的一般方法如下。
1)建立与数据库的连接。
2)通过DataAdapter对象从数据库中取出需要的数据。
3)实例化一个SqlCommandBuilder类对象,并为DataAdapter自动生成更新命令。
4)使用DataAdapter对象的Fill()方法填充DataSet。
5)使用NewRow()方法向DataSet中填充的表对象中添加一个新行。
6)为新行中的各字段赋值。
7)将新行添加到DataSet中填充的表对象中。
8)调用DataAdapter对象的Update()方法,将数据保存到数据库。
例如,下列代码实现了向StudentDB数据库的StudentInfo表中添加一条新记录。
需要说明的是,使用SqlCommandBuilder对象自动生成DataAdapter对象的更新命令(DeleteCommand、InsertCommand和UpdateCommand)时,填充到DataSet中的DataTable对象只能映射到单个数据表或从单个数据表生成,而且数据库表必须定义有主键。所以通常把由SqlCommandBuilder对象自动生成的更新命令称为“单表命令”。
10.7.4 修改记录
通过DataSet修改现有数据表记录的操作方法与添加新记录非常相似,唯一不同的地方是无须使用NewRow()方法添加新行,而是创建一个DataRow对象后,从表对象中获得需要修改的行并赋给新建的DataRow对象,根据需要修改各列的值(为各字段赋以新值)。最后仍需要调用DataAdapter对象的Update()方法将更新提交到数据库。
例如,下列代码按照指定“学号”字段值返回需要修改的记录,修改数据后将修改结果提交到数据库,从而完成修改记录的操作。
10.7.5 删除记录
使用DataSet从填充的表对象中删除行时需要创建一个DataRow对象,并将要删除的行赋值给该对象,然后调用DataRow对象的Delete()方法将该行删除。当然此时的删除仅是针对DataSet对象的,若需从数据库中删除该行,还需要调用DataAdapter对象的Update()方法将删除操作提交到数据库。
“删除记录”按钮被单击时执行的事件代码如下。
10.8 实训——设计一个课程表管理程序
10.8.1 实训目的
1)通过本实训进一步理解使用DataSet配合DataAdapter和DataReader对象完成数据库常规操作的一般步骤。
2)掌握ASP.NET标准控件的基本使用方法和常用属性。
3)本实训除应用到了DataSet、DataAdapter和DataReader等ADO.NET对象外,还涉及了许多SQL查询语句,以及通过ASP.NET内置对象在不同页面间传递数据的技巧,这些都是开发Web数据库应用程序的基本手段,要求在实训中认真理解其含义及语句书写格式。
10.8.2 实训要求
在ASP.NET环境中使用DataSet配合DataAdapter对象,创建一个简单的学校课程表管理程序,具体功能要求如下。
1.查询某班级课程表
程序启动后显示如图10-12所示的页面(Default.aspx),用户可以在下拉列表框中选择希望查询课程表的班级名称。当下拉列表框中的被选内容变化或用户单击“确定”按钮时,打开如图10-13所示的页面(CurriCulum.aspx),显示指定班级的课程表。单击IE浏览器工具栏上的“后退”按钮 可返回上一页面。
图10-12 选择班级
图10-13 查询课程表
2.更新或添加课程表
在Default.aspx页面的下拉列表框中选择“管理员”选项,将自动打开如图10-14所示的课程表管理页面(Admin.aspx),但此时课程表编辑环境中所用到的所有控件(“班级”文本框、“提交”按钮和提供课程选项的所有下拉列表框)均不可见。
用户在“请输入密码”文本框中输入了密码“123456”后,单击“确定”按钮,这些控件方可使用,此时密码输入框自动隐藏。在如图10-15所示的页面中,用户可在文本框中输入班级名称,并通过表格中的相应位置上的下拉列表框选择课程名称。课程表编排完毕后单击“提交”按钮,将数据上传到数据库中。
单击“提交”按钮后,程序将分析用户输入的班级名称,若为新班级则执行插入记录操作,若为已存在的班级则执行更新操作。此时如果用户忘记了输入班级名称,程序将给出提示。
图10-14 输入管理员密码
图10-15 编辑课程表
10.8.3 实训步骤
1.创建数据库及表
在Access中创建一个名为“curriculum.mdb”的数据库,并在库中创建两个数据表:syllabus和course。syllabus表中包括如图10-16所示的16个字段,class字段用于存放班级名称,c11用于存放星期一的1-2节课,c21用于存放星期二的1-2节课,c31用于存放星期三的1-2节课,c12用于存放星期一的3-4节课……注意将class字段设为主键。
图10-16 syllabus表的结构
course表中只有一个curriculumname字段,用于存放课程名称。数据库创建完毕后,输入一些记录并保存。
2.向网站中添加类文件(MyClass.cs)
为了提高代码的复用率,将数据库连接对象、数据库操作等代码放置在类文件中。在解决方案资源管理器中右击网站名称,在弹出的快捷菜单中选择“添加”→“添加新项”命令,向网站中添加一个名为MyClass的类文件,系统会自动创建App_Code文件夹,并将该文件放置在其中。MyClass.cs中包含GetDT()、ClasIsExist()、Updata()和Insert()共4个静态方法,其代码如下所示。
1)添加需要的命名空间引用,代码如下。
using System.Configuration;
using System.Data.OleDb;
using System.Data;
2)编写类代码如下。
3.设计选择班级页面(Default.aspx)
1)新建一个ASP.NET空网站,将前面设计完毕的数据库文件curriculum.mdb复制到网站的App_Data文件夹中。向网站中添加一个Web窗体页Default.aspx,并向其中添加必要的说明文字和添加一个下拉列表框控件DropDownList1。设置DropDownList1的ID属性为drop-Class,AutoPostBack属性为True。
2)参照前面介绍过的方法,将用于连接Access数据库的连接字符串写到web.config中。
3)切换到Default.aspx页面的代码编辑窗口,添加所需命名空间的引用,代码如下。
using System.Data();
4)Default.aspx页面载入时执行的事件代码如下。
5)“请选择一个班级”下拉列表框中的选项改变时执行的事件处理代码如下。
4.设计课表查询页面(curriculum.aspx)
1)向网站中添加一个新Web窗体,并将其命名为curriculum.aspx。向页面中添加一个运行在服务器端的Table控件。
2)切换到curriculum.aspx页面的代码编辑窗口,添加所需命名空间的引用,代码如下。
using System.Data();
3)curriculum.aspx页面载入时执行的事件代码如下。
5.设计编辑课程表页面(Admin.aspx)
1)向网站中添加一个新Web窗体,并将其命名为Admin.aspx。按照图10-17所示向页面中添加两个容器控件Panel1和Panel2。向Panel1中添加一个文本框TextBox1和一个命令按钮控件Button1。向Panel2中添加一个用于布局的HTML表格,按图10-17所示添加一个文本框TextBox2和两个命令按钮控件Button2、Button3,添加15个用于提供备选课程名称的下拉列表框DropDownList1~DropDownList15(注意,添加控件时应按编号横向排布)。
图10-17 设计Admin.aspx页面
2)设置TextBox1的ID属性为txtPwd;设置Button1的ID属性为btnOK,Text属性为“确定”;设置TextBox2的ID属性为txtClass;设置Button2、Button3的ID属性分别为btnSubmit和btnBack,Text属性分别为“提交”和“返回”,Button3的PostBackUrl属性为“Default.aspx”,使用户单击按钮时能返回到指定的页面。
3)为了统一处理所有下拉列表框的SelectedIndexChanged事件,需要切换到Admin.aspx的源视图,在所有下拉列表框的定义中添加下列用于创建控件共享事件的代码。
OnSelectedIndexChanged="DropDownList_SelectedIndexChanged"
4)切换到Admin.aspx页面的代码编辑窗口,添加对所需命名空间的引用,代码如下。
usingSystem.Data;
5)在所有事件处理程序之外声明用于存放下拉列表框中用户选项的静态数组,代码如下。
static string[]DropText=new string[16];//声明静态字符串型数组,存放下拉列表框中的选项
6)Admin.aspx页面载入时执行的事件代码如下。
7)“确定”按钮被单击时执行的事件代码如下。
8)“提交”按钮被单击时执行的事件代码如下。
9)下拉列表框控件组的共享SelectedIndexChanged事件代码如下。
下面来思考以下几个问题。
1)用户选择“新建课程表”时需要输入管理员密码,而本例将密码以常数的方式写到了代码中,用户不能修改。如何修改程序使用户可以修改密码?
2)程序仅提供了新建课程表的功能,并且当新建课程表对应的班级已存在时采用更新方式处理(新课表替换老课表)。如何修改程序使之具有修改课程表的功能?
3)本程序仅提供了手工排课的功能。如果希望能根据用户需求自动生成课程表,应如何修改?