1、在目前的主流软件开发中,信息管理或处理系统占据了主要方面,所以几乎所有平台都支持数据库访问技术,.NET平台自然也不例外。ADO.NET是.NET框架中的一个重要组件,以一种统一的访问方式来连接多种形式的数据源,其中最常见的就是关系型数据库。ADO.NET采用相同的方法访问本地数据库、客户机/服务器数据库和基于Internet的高度分布式数据库。本章讲述.NET开发技术中应用非常广泛的核心开发技术ADO.NET数据访问技术。16.1 数据库开发基础16.2 使用ADO.NET16.3 ADO.NET综合应用16.4 实例:图书信息管理16.1.1 SQL Server 201916.1.1 S
2、QL Server 2019SQL Server是一个全面的、集成的、端到端的数据解决方案,它为企业中的用户提供了一个安全、可靠和高效的平台,用于企业数据管理和商业智能应用。首先建立数据库之后添加表结构和为表添加列。最后可以直接为表添加数据。SQL全称是结构化查询语言(S Structured Q Query L Language),是数据库中使用的标准数据查询语言。目前,绝大多数流行的关系型数据库管理系统都采用了SQL语言标准。虽然对SQL语句进行了再开发和扩展,但是包括Select、Insert、Update、Delete、Create以及Drop在内的标准的SQL命令仍然可以被用来完成几
3、乎所有的数据库操作。Insert操作用于向数据库表中插入记录。INSERT INTO table(column ,column)VALUES(columnvalue,columnvalue);下面语句是向BookInfo表中插入一行记录。INSERT INTO BookInfoVALUES(9787040074949,数据库系统概论,萨师煊,高等教育出版社,28.0,2000-2-1);Select操作从一个或多个表中选取特定的行和列。因为查询和检索数据是数据库管理中最重要的功能,所以SELECT语句在SQL中是工作量最大的部分。SELECT语句的结果通常是生成另外一个表。SELECT col
4、umns FROM tables ORDERE BY column ASC|DESC WHERE predicates下面语句将查询BookInfo表中作者谭浩强的书。SELECT NameFROM BookInfoWHERE Author=谭浩强UPDATE语句允许用户在已知的表中对现有的行进行修改。例如,下面语句是一个单行更新。UPDATE BookInfoSET Name=C语言程序设计(第三版)WHERE ISBN=9787302108535DELETE语句用来删除已知表中的行。如同UPDATE语句中一样,所有满足WHERE子句中条件的行都将被删除。由于SQL中没有UNDO语句或是“你
5、确认删除吗?”之类的警告,在执行这条语句时千万要小心。例如,下面语句将删除所有清华出版社的书的信息。DELETE FROM BookInfoWHERE press=清华出版社ADO.NET目前版本为3.0,用于访问和操作数据的两个主要组件是.NET Framework数据提供程序和DataSet。ADO.NET用不同的数据提供者来操作不同数据源数据提供者实际上是ADO.NET提供的一组封装好的类,用于操作不同类型的数据库系统。我们可以把这些数据提供者想象成我们程序与数据库之间的一座桥梁,我们的程序通过它和数据库打交道。SQL Server数据提供者:为操作SQL Server数据库(SQL S
6、erver 7.0及之后版本)提供完善的支持。OLE DB数据提供者:为所有支持OLE DB驱动的数据源提供支持,它可以支持SQL Server 7.0之前的版本。Oracle数据提供者:为操作Oracle数据库(8i及之后版本)提供完善的支持。ODBC数据提供者:为操作Access数据库及其他支持ODBC驱动的数据源提供支持。命名空间描述System.Data包含columns,relations,tables,datasets,rows,views和constraints等这些主要的数据容器类System.Data.Common主要包含ADO.NET中的基类,大多数的抽象类,以及ADO.N
7、ET中的一些核心函数。并实现了System.Data中的部分接口。不同的数据驱动程序从这些类中继承并构建它们自己的功能(比如DbConnection,DbCommand等)System.OleDb包含用于连接提供OLE DB驱动的数据源的所有累,包括OleDbCommand,OleDbConnection,OleDbDataReader,和 OleDbDataAdapter。这些类支持大多数的OLE DB数据源,但是不能支持那些使用了OLE DB2.5 版接口的数据源System.Data.SqlClient包含了连接SQL Server数据库的类,包括SqlDbCommand,S q l D
8、 b C o n n e c t i o n,S q l D a t a R e a d e r,a n d SqlDBDataAdapterSystem.Data.OracleClient包含了连接Oracle数据库(8i或之后版本)所需要的类,包 括 O r a c l e C o m m a n d,O r a c l e C o n n e c t i o n,OracleDataReader,和 OracleDataAdapterSystem.Data.Odbc包 含 连 接 O D B C 数 据 库 所 需 的 类,这 些 类 包 括:OdbcCommand,OdbcConnec
9、tion,OdbcDataReader,和 OdbcDataAdapterSystem.Data.SqlTypes包含与SQL Server中的数据类型相对应的数据结构,这些数据类型并不强制使用,如果使用了标准的.NET数据类型话,系统会自动将他们转化成匹配的Sql 数据类型我们需要根据所使用数据库系统的不同,选择相对应的数据提供者,并在代码中包含相应的命名空间。ADO.NET中使用Connection类完成数据库的连接。只有建立了连接之后,才能对数据库表中的记录进行添加、删除、修改、读取之类的功能。要建立一个数据库连接,首先需要提供一个连接字符串。连接字符串的格式是一个以分号为界,划分键值参
10、数对的列表。这个串中包含了数据库的位置,想要连接的数据库的名称以及数据库的身份验证方式等众多的信息。必须提供的信息有以下3个。数据库所在服务器的位置(Data Source)。数据库的名称(Initial Catalog)。数据库的身份验证方式(Windows集成验证或者是SQL Server身份验证)。下面是一个用于连接16.1.2节创建的SQL Server数据库VBNET2019的连接字符串,采用Windows集成验证的方式。Dim connectionString As String=Data Source=localhost;Initial Catalog=VBNET2019;Int
11、egrated Security=SSPI;关于更多的数据库连接字符串,可参考connectionstrings。连接字符串确定后,需要将其传递给Connection类的实例的ConnectionString属性,然后调用其Open方法打开该连接。这里以SqlConnection类为例。1 Dim connectionString As String=Data Source=server;Initial Catalog=VBNET2019;Integrated Security=SSPI;2 Dim conn As SqlConnection=new SqlConnection()3 conn
12、.ConnectionString=cString4 conn.Open()如果是Windows窗体应用程序,则需要先添加该配置文件。单击菜单栏中的【项目】【添加新项】,打开【添加新项-WindowsApplication1】窗口,选择常规类别中的应用程序配置文件。在app.configue文件中加入一个connectionStrings标签,代码如下。1 2 3 4 5 6 7 可以通过ConfigurationManager.ConnectionStrings集合通过名字将其取出。使用该集合,首先要添加System.Configuration命名空间的引用。下面代码通过Configurat
13、ionManager.ConnectionStrings集合取出了连接字符串。Dim conn string As String=ConfigurationManager.ConnectionStrings(MyConnectionString).ConnectionString如果是ASP.NET项目,则可以将数据库连接字符串写入Web.Config文件。方式与Windows窗体应用程序中的app.config文件类似,添加一个connectionstring标签即可。在需要用到连接字符串的时候,可以从WebConfigurationManager.ConnectionStrings集合中通
14、过名字将它取出来。要使用这个集合的话,同样需要在项目中添加对System.Configuration命名空间的引用。.NET中执行数据库操作是通过Command类来完成的。Command类是一个执行SQL语句和存储过程的类。通过它,我们可以实现对数据的添加、删除、更新、查询等各种操作。首先要确定Command的种类,是SqlCommand,OracleCommand,还是其他类型。在确定了Command类型之后,需要把为它指定一个Connection。另外需要指定CommandType 和CommandText。这3项信息可以在创建Command对象的时候以参数的形式传到Command的构造函
15、数中,也可以通过属性的方式进行设置。需要说明的是,CommandText既可以是SQL语句,也可以是存储过程,也可以是数据表的名字。使用不同种类的CommandText,需要设置相应的CommandType。类型描述CommandType.TextCommandType的默认值。它指示执行的是SQL语句。为CommandText指定SQL字符串CommandType.StoredProcedure指 示 执 行 的 是 存 储 过 程,需 要 为CommandText指定一个存储过程的名称CommandType.TableDirect指示执行的是得到某个数据表中的所有数据。此时需要为Comma
16、ndText指定一个数据表名称。(通常情况下还是直接写SQL语句来实现这个功能比较可靠)下面的代码演示了如何使用SqlCommand对象执行一个SQL查询语句,采用了属性设置的方式指定了必须的3项信息,代码如下。1 Dim cmd As SqlCommand=new SqlCommand()2 cmd.Connection=con3 cmd.CommandType=CommandType.Text4 cmd.CommandText=SELECT*FROM BookInfo“事实上,上面代码还可以写成更简洁的形式,如下。Dim cmd As SqlCommand=new SqlCommand(S
17、ELECT*FROM BookInfo,con)如果需要执行存储过程,可以使用下面这种方式。Dim cmd As SqlCommand=new SqlCommand(GetBooks,con)cmd.CommandType=CommandType.StoredProcedure至此,我们只是简单地定义出了Command对象,并为它们设置了一些属性,但还并没有真正的执行这些操作。如果想要执行Command操作的话,还需要调用其他方法来执行。我们有3种方法可以使用,这3种方法的主要不同之处在于它们的返回值不同。ExecuteNonQuery():用于执行非SELECT的命令,如INSERT,DEL
18、ETE或者UPDATE命令,返回一个命令所影响的数据的行数。也可以用ExecuteNonQuery()方法来执行一些数据定义命令,如新建,更新,删除数据库对象(如表,索引等)。ExecuteScalar():用于执行SELECT查询命令,返回数据中第一行第一列的值。这个方法通常用来执行那些用到COUNT()或者SUM()函数的SELECT命令。ExecuteReader():用于执行SELECT命令,并返回一个DataReader对象。这个DataReader是前向只读的数据集。DataReader是一种非常有用的数据读取工具,提供一种连接着数据库、只能向前的记录访问方式,它可以执行SQL语句
19、或者存储过程。DataReader是一个轻量级的数据对象,如果只是需要将数据读出并显示的话,那么它就是最合适的工具DataReader在读取数据的时候要求数据库保持在连接状态,读完数据之后才能断开连接。方法名返回类型说明CloseVoid关闭数据读取器。已重载NextResultBoolean当读取批量的SQL语句的结果时,前进到下一个结果集,如果有更多的结果集,将返回True。有重载ReadBoolean前进到下一条记录。如果有更多的记录,将返回TrueGetNameString取得指定字段的字段名称GetOrdinalInt取得指定字段名称在纪录中的顺序GetValueObject取得指定
20、字段的数据1 Private Sub Form1_Load(ByVal sender As System.Object,ByVal e As System.EventArgs)Handles MyBase.Load2 创建Connection对象和Command对象 3 Dim connectionString As String=ConfigurationManager.ConnectionStrings(MyConnectionString).ConnectionString4 Dim con As New SqlConnection(connectionString)5 Dim cmd
21、As New SqlCommand(SELECT*FROM BookInfo,con)6 Dim reader As SqlDataReader7 Try8 con.Open()打开数据库连接9 执行ExecuteReader()方法返回的是一个DataReader对象10 reader=cmd.ExecuteReader()11 While reader.Read()遍历DataReader时最常用的方式12 Label1.Text&=reader(0)&vbTab&reader(1)&vbCrLf 按照顺序或列名指定要读取的项13 End While14 Catch ex As SqlEx
22、ception15 Throw New ApplicationException(ex.ToString()执行过程中发生异常的话,抛出异常16 Finally17 reader.Close()关闭DataReader18 con.Close()断开数据库连接19 End Try20 End Sub通过这段代码,我们得到了一个填充了数据的DataReader对象。在需要显示这些数据的地方,就可以通过读DataReader的方式来显示这些数据了。Close方法可以关闭DataReader对象和数据源之间的联系。在读完数据之后务必把DataReader对象关闭,否则当DataReader对象尚未关
23、闭时,DataReader所使用的Connection对象就无法执行其他操作。DataSet是实现基于非连接的数据查询的核心组件。可以将DataSet看成是一个数据容器,将数据库中的数据拷贝了一份放到了用户本地的内存中,供用户在不连接数据库的情况下读取数据,DataSet对象表示数据库中数据的缓存,具有类似数据库的结构,如表、列、关系和约束等。下面代码创建了一个DataSet,创建时可以选择指定一个名称参数。如果没有为DataSet指定名称,则该名称会设置为“NewDataSet”。Dim books As DataSet=New DataSet(Books)使用DataSet,就一定会用到D
24、ataAdapter类。这也是一个非常常用的类,因为DataSet必须通过DataAdapter才能得到数据。因为DataSet并不能直接跟数据源产生联系,这种联系必须通过DataAdapter来完成。一般有两种。一种是通过Command对象执行SQL语句,将获得的结果集填充到DataSet对象中;另一种是将DataSet里更新数据的结果返回到数据库中。Fill方法:用于填充或刷新DataSet。Update方法:将DataSet中的数据更新到数据库里。应用背景:某学院的图书资料室需要设计一个图书信息管理的程序,能够显示该资料室的图书的信息,并实现对图书的添加、删除、更新,并且可以对图书进行查
25、询。字段名数据类型ISBNVarchar(13)NameVarchar(100)AuthorVarchar(10)PressVarchar(20)PricemoneyPressDateSmallDateTime真正进行项目开发时,我们并不会把对数据库的操作代码嵌入到业务逻辑代码中。它们一般都是分开存放的。当逻辑代码需要数据的时候,它通过调用专门的数据库操作代码来得到这些数据。这个可以简单的理解成业务的分层:不同的代码负责不同的功能,互不干扰。这样做对代码的维护和代码的重用都非常的有利。到目前为止比较成熟并被众多开发者所采用的是这样一种模式:为数据库中每一个主要的表配置一个专门的数据信息类,这个
26、类中包含这个表中的数据信息。另外为这个表提供一组它要用到的方法,如保存数据、删除数据、更新数据等方法,将这些方法也封装在一个类中。这个数据类非常简单,每个属性都与数据表中的一项对应。1 Public Class BookInfoDB2 3 End Class这个类暂时是空的,下面所有针对数据库表BookInfo的操作,我们都会添加到这个类中。首先在项目中添加应用程序配置文件app.config,在该配置文件中配置上连接字符串1 2 3 将这个字符串定义成一个全局变量,这样整个类中需要用到它的地方都可以方便的调用。常见的方式是将这个获取字符串内容的代码放到数据操作类的构造函数中。1 Public
27、 Class BookInfoDB2 Public connectionString As String3 Public Sub New()4 connectionString=System.Configuration.ConfigurationManager.ConnectionStrings(Book).ConnectionString5 End Sub6 End Class在BookInfoDB类中添加GetBooks()的函数,用于查询所有图书的信息。1 批量查询2 Public Function GetBooks()As DataSet3 Dim con As New SqlConn
28、ection(connectionString)4 Dim ds As New DataSet()5 假设我们想要得到所有的图书信息6 Dim cmd As New SqlCommand(SELECT*FROM BookInfo,con)7 Dim adp As New SqlDataAdapter(cmd)8 Try9 con.Open()10 adp.Fill(ds)将数据填充到DataSet中11 Catch ex As SqlException12 Throw New ApplicationException(Data error.)执行过程中发生异常的话,抛出异常13 Finally
29、14 con.Close()关闭数据库连接15 End Try16 Return ds 返回数据集17 End Function查询单条数据需要首先指定我们想要得到哪条学生信息。这里我们通过指定图书的名称来确定具体的那条记录。1 单个查询2 Public Function GetBookByName(ByVal name As String)As BookDetails3 定义BookDetails对象,用于存储图书信息4 Dim book As New BookDetails5 Dim reader As SqlDataReader6 Dim con As New SqlConnection
30、(connectionString)7 Dim cmd As New SqlCommand(,con)8 cmd.CommandText=SELECT*FROM BookInfo WHERE Name=&name&9 Try10 con.Open()11 调用Command对象的ExecuteReader方法12 reader=cmd.ExecuteReader()13 循环读取所有数据,实际上这里只有一条记录14 While reader.Read 15 根据列名取出不同列的数据16 book.ISBN=reader(ISBN)17 book.Name=reader(Name)18 book
31、.Author=reader(Author)19 book.Press=reader(Press)20 book.Price=reader(Price)21 book.PressDate=reader(PressDate)22 End While23 reader.Close()24 Catch ex As SqlException25 如果读取数据过程中发生异常的话,将book对象设置成空26 book=Nothing 27 Throw New ApplicationException(ex.Message)28 Finally29 con.Close()30 End Try31 Retur
32、n book32 End Function我们在添加图书数据的时候,先用BookDetails这个类的对象来收集图书信息,然后再把它们集中送到BookInfoDB类中进行处理,代码如下。1 添加记录2 Public Function InsertNewBook(ByVal book As BookDetails)As Integer3 Dim affectRow As Integer=04 Dim con As New SqlConnection(connectionString)5 Dim cmd As New SqlCommand(,con)6 cmd.CommandText=String
33、.Format(INSERT INTO BookInfo(ISBN,Name,Author,Press,Price,PressDate)VALUES(0,1,2,3,4,5),book.ISBN,book.Name,book.Author,book.Press,book.Price,book.PressDate)7 Try8 con.Open()9 调用ExecuteNonQuery方法,返回SQL语句所影响到的行数,据此我们可以判断SQL语句执行是否成功10 affectRow=cmd.ExecuteNonQuery()11 Catch ex As SqlException12 affectRow=0 发生异常的话,将影响的行数值设置成0,说明没有插入成功13 Throw New ApplicationException(ex.Message)14 Finally15 con.Close()16 End Try17 Return affectRow18 End Function数据库设计: