DataAdapter
如果想将查询结果存储在DataSet或DataTable对象中,比较简单的是使用DataAdapter对象。
1 DataAdapter对象
DataAdapter类是ADO.NET对象模型中联机和脱机这两部分的桥梁。
l DataAdapter能将数据从数据库取出,再放到DataSet中;
l DataAdapter还可以提取DataSet中存储在缓存中的更新数据,并提交给数据库
1.1 DataAdapter特性
DataAdapter对象,感觉上,好像与ADO的Command对象、RDO的rdoQuery对象、DAO的QueryDef对象相似;但是它们的差异明显。
1.1.1 DataAdapter的设计目的是处理脱机数据
DataAdapter是专门为处理脱机数据而设计的。
最好的例子是Fill方法,调用Fill方法可以不需要DataAdapter拥有与数据库间的活动连接:若在当前未开放的连接上调用Fill,DataAdapter会开发连接、查询数据库,然后填充结果到DataSet,最后关闭连接。
1.1.2 DataAdapter与DataSet间没有直接连接
在DataAdapter对象的Fill方法中传递DataSet对象作为参数,可以填充DataSet中的DataTable。
l 代码:
OleDbDataAdapter.Fill(DataSet)
调用Fill完成后,两个对象之间就没有连接了。(DataSet不会保存任何内在的或外在的对DataAdapter的引用;DataAdapter也不会保留对DataSet的引用);DataSet不包含任何表明数据来源的信息(例如:连接字符串、表格名、列名称)。
因此,可以将DataSet对象从中间层服务器传递到客户端应用程序,不必担心泄露关于数据库位置与结构的信息。
1.1.3 DataAdapter中能包含回递给数据库的更新逻辑(存储在DataSet中)
DataAdapter也可以向数据库回递待定的更改。(这是与过去的数据访问模型的主要区别)
使用DataAdapter的Update方法提交更改。调用时需要DataSet对象作为参数。(DataSet能够缓存更改)
注意:实际的更新逻辑包含在DataAdapter对象中。
1.1.4 控制DataAdapter的更新逻辑
在DataAdapter中,可以自定义insert、update、delete查询,或使用存储过程提交更新。
DataAdapter拥有四项属性包含了Command对象,通过它们就可以分别指定自己的操作查询或存储过程;同样也可以分别指定能将数据在DataSet和存储过程之间来回移动的参数。
1.2 DataAdapter剖析(研究它的结构以及运作方式)
DataAdapter的设计目的在于帮助你将查询结果存储在DataSet对象和DataTable对象之中。
Command对象允许通过DataReader对象浏览查询结果。
DataAdapter对象由一系列Command对象组成,还包括一组用于确定DataAdapter与DataSet通信关系的映射属性。
1.2.1 子命令
使用DataAdapter将查询结果存储在DataSet中时,DataAdapter会使用一个Command对象来与数据库通讯;DataAdapter对象内在的使用了DataReader来取得结果数据,然后将信息复制到DataSet的新行去。(用循环逐行)
DataAdapter对象取数据的Command对象在其SelectCommand属性中。
插入数据的Command在其InsertCommand属性;删除数据的Command在其DeleteCommand属性;更新数据的Command在其UpdateCommand属性。
1.2.2 TableMapping集合
默认时,DataAdapter假设DataReader中的列与DataSet中的列是相匹配的。
如果,你希望DataSet的架构与数据库中的架构不同(一种可能是想给DataSet中的特定列使用不同的名称):传统作法是,在查询中给列指定别名;现在,你可以使用TableMappings集合机制。
DataAdapter对象的TableMappings集合允许创建一个数据库和DataSet之间的映射层。
TableMappings属性返回一个DataTableMappingsCollection对象。
DataTableMappingsCollection对象含有一组DataTableMapping对象,其中每个对象都允许你在数据库中的某一个表(或视图、存储过程)与DataSet相应表之间创建映射。
而DataTableMapping对象含有一个ColumnMappings属性,它返回一个DataColumnMappingCollection对象。
DataColumnMappingCollection对象是由一组DataColumnMapping对象组成的集合,每一个DataColumnMapping都能将数据库一列映射为DataSet一列。
l 代码演示(填充TableMappings集合)
Dim da As OleDbAdapter
‘初始化Adapter
Dim tblMap as DataTableMapping
Dim colMap as DataColumnMapping
‘将表名对应
tblMap=da.TableMappings.Add(“Table”,”Employee”)
‘将列名对应
colMap=tblMap.ColumnMappings.Add(“EmpID”,”EmployeeID”)
colMap=tblMap.ColumnMappings.Add(“LName”,”LastName”)
colMap=tblMap.ColumnMappings.Add(“FName”,”FirstName”)
2 创建和使用DataAdapter对象
2.1 创建DataAdapter
注意:创建DataAdapter对象时,一般都希望SelectCommand属性是可用的Command对象。
l 代码
Dim strConn as String=”…”
Dim cn as new OleDbConnection(strConn)
Dim ssql as string=”select CustomerID,CompanyName from Customers”
Dim cmd as new OleDbCommand(ssql,cn)
Dim da as new OleDbAdapter()
da.SelectCommand=cmd
2.2 DataAdapter的构造函数
DataAdapter有4个构造函数(3个是带参数的):
l Public Sub New(ByVal selectCommand As OleDbCommand)
使用一个存在的Command作为SelectCommand使用
l Public Sub New(ByVal selectCommandText As String, ByVal selectConnection As OleDbConnection)
使用一个存在的Connection连接对象,并根据传入的查询SQL潜在的构造SelectCommand对象
l Public Sub New(ByVal selectCommandText As String, ByVal selectConnectionString As String)
此构造函数,潜在的为新DataAdapter构造一个Connection对象,并潜在的构造SelectCommand对象。
2.3 从查询中取得结果
2.3.1 使用Fill方法
调用Fill会:
1. 执行存储在SelectCommand属性中的查询
2. 将查询结果存储在DataSet中,如果未指定,则数据放在新创建的默认的DataTable对象(名Tabe)
n 代码:
Dim strConn,strSql as String
strConn=”…”
strSql=”Select CustomerID,CompanyName,ContactName,Phone from Customers”
Dim da as New OleDbDataAdapter(strSql,strConn)
Dim ds As new DataSet()
da.Fill(ds)
2.3.2 使用Fill方法创建DataTable对象和DataColumn对象
Fill方法会在DataSet对象中创建新的DataTable(如果没有指定DataTable名的话默认为‘Table’)此DataTable的列结构与查询结果相同。
在TableMappings集合中添加新TableMapping项目可以修改查询结果,改变其映射关系。
n 代码
Dim da as new OleDbDataAdapter(strSql,strConn)
Da.TableMappings.Add(“Table”,”Customers”) ‘此处将查询结果映射到名叫Customers的DataTable中
Dim ds as new DataSet()
da.Fill(ds)
2.3.3 使用重载的Fill方法
Fill的不同重载版本可以拥有更多的控制权。
2.3.3.1 指定DataTable
有两个Fill重载版本可以指定DataTable(这使你更灵活的控制DataTable)
2.3.3.1.1 填充时指定表名称
DataAdapter.Fill(DataSet,”MyTableName”)
2.3.3.1.2 填充时指派DataTable对象(而非DataSet)
DataAdapter.Fill(DataTable)
2.3.3.2 根据DataAdapter对象的Fill方法分页
Fill的一个重载版本可以获取查询结果的一部分(实现一部分分页的效果)
DataAdapter.Fill(DataSet,intStartRec,intRecCount,”Table Name”)
但是,你要牢记一点,即使你指定返回的是一部分结果,Fill方法还是将所以查询结果都返回,只不过将不需要的删去了。
所以,Fill方法简化查询分页的效率很一般。
2.3.3.3 用DataAdapter对象将一个Recordset的内容填充到DataSet对象
OLEDB.NET数据提供者的两个Fill重载版本,可以将数据从ADO Recordset复制到ADO.NET的DataSet中
2.3.3.3.1 OleDbAdapter.Fill(DataSet,AdoRecordset,”Table Name”)
2.3.3.3.2 OleDbAdapter.Fill(DataTable,AdoRecordset)
2.3.4 开放和关闭连接
n DataAdapter对象和Command对象处理Connection对象的方法存在差异。
Command对象需要一个开放的Connection对象,否则Command会有异常。
DataAdapter对象没有这个限制。
调用DataAdapter.Fill方法(SelectCommand的Connection是关闭状态),DataAdapter会先开放连接,然后提交查询、取结果,最后关闭连接。
有时候,连续的使用DataAdapter的Fill方法,会使Connection对象连续的开放和关闭。怎么办?你可以给多个DataAdapter对象指定同一个Connection对象,并且在使用Fill方法之前,就用Connection.Open方法打开连接。
2.3.5 多次调用Fill方法
想一想:如何刷新DataSet中的数据,以便使用户能看到即时的更改。
最简单的办法就是清空DataSet或者DataTable,然后再次调用DataAdapter的Fill填充。
假使没有清空就连续调用(两次)Fill方法,那么你会在DataSet或DataTable中看到多个(两个)客户记录
对代码段来说,DataAdapter不能知道哪些客户是重复的。
通常,数据库的表中会定义一个主键。这样,能防止用户创建重复的行。
DataTable对象也有一个PrimaryKey属性。如果DataAdapter所填充的DataTable有一个主键,那么DataAdapter就会利用该主键来确定哪些行是重复的。(因此,在第一次填充后定义了DataTable的主键,那么第二次的填充DataAdapter会定位重复的行,删去旧值。)
下面假设第一次填充时,有一个客户记录被添加到DataTable中,之后,该客户记录被从数据库中删除了;此时如果进行第二次填充,客户记录会更新吗?不会,DataAdpater在查询结果中找不到该客户的信息,而且也不会将它从DataTable中删除。
想一想:怎么刷新全部数据?要先清空DataSet或DataTable,然后再次调用DataAdapter.Fill方法。(这样能确保没有重复行,同时确保数据库中没有的行不添加进DataSet)
2.4 将查询结果映射到DataSet中
使用DataAdapter对象的TableMappings集合
2.4.1 DataAdapter对象的TableMappings集合
TableMappings集合用来控制DataAdapter将DataSet映射到数据库的方式。
填充时,将TableMappings保留为空,调用Fill,然后将DataSet作为参数传递,且不指定表名称,DataAdapter会假定你要处理的是(名为Table)的DataTable
TableMappings属性返回一个类型为DataTableMappingCollection的对象。该对象内含多个DataTableMapping对象。可以如下添加:
DataAdapter.TableMappings.Add(“Table”,”Employees”) ‘告知DataAdapter与名为Employees的DataTable通讯
创建表映射之后,下一步可以创建列映射。例如:
Dim da as OleDbDataAdapter
Dim tblMap as DataTableMapping
Dim colMap as DataColumnMapping
tblMap=da.TableMappings.Add(“Table”,”Employees”)
colMap=tblMap.ColumnMappings.Add(“EmpID”,”EmployeeID”)
colMap=tblMap.ColumnMappings.Add(“Lname”,”LastName”)
colMap=tblMap.ColumnMappings.Add(“Fname”,”FirstName”)
2.4.2 MissingMappingAction属性
绝大多数情况下,开发人员在DataSet中都会使用与数据库中表相同的列名称。
当DataAdapter检查查询结果时,若发现某一列在其映射集合中不存在,它会检查MissingMappingAction属性以确定如何处理这些列。
MissingMappingAction属性接受System.Data名称空间中MissingMappingAction枚举类型的值。
默认值为Passthrough,表示DataAdapter将缺失的列映射为DataSet中同名的列;
值Ignore,表示忽略缺失的列;
值Error,表示若有缺失列时,会产生异常。
2.5 处理批查询
MS-Sql Server允许执行批查询。
Select CustomerID,CompanyName,ContactName,Phone from Customers where CustomerID=”ALFKI”
Select OrderID,CustomerID,EmployeeID,OrderDate from Orders where Customer=”ALFKI”
如果使用DataAdapter.Fill(DataSet)的方式取得DataSet,将会在DataSet中产生两个DataTable对象。每个DataTable保存一个查询的结果。一个名叫“Table”,一个名叫“Table1”
2.6 从存储过程中获取行
假设是Sql Server的存储过程(它能返回结果集)如下:
Create procedure GetAllCustomers as
Select CustomerID,CompanyName,ContactName,Phone from Customers
Return
使用DataAdapter允许它并取得结果集放入DataSet或DataTable中。你可以使用下面的查询语句(两个)之一:
{CALL GetAllCustomers}或EXEC GetAllCustomers
另外,如果需要使用Command对象的CommandType属性作为DataAdapter的SelectCommand的话,还可以有更多的控制功能。
对于Oracle的存储过程,它不能从查询中返回行,仅能通过输出参数返回数据。
(Oracle的OLEDB提供者和Oracle的ODBC驱动程序允许开发者调用Oracle的存储过程,并通过输出参数取得查询结果。)
因此,Oracle的查询语法如下:
{CALL PackageName.ProcName (?, {resultset 20, OutParam1, OutParam2, …})}
2.7 获取架构信息
设计DataTable的目的在于加强数据上的约束,比如主键、字符串字段最大长度以及可空性限制等。
多数情况下,开发者不需要这些信息,DataAdapter能通过一些关键属性取得架构信息——比如MissingSchemaAction属性和FillSchema方法
2.7.1 MissingSchemaAction属性
Fill方法使用的是不包含架构信息的DataSet对象和DataTable对象。
默认情况下,DataSet、DataTable没有那些列,DataAdapter会添加列信息来存储查询结果,这个操作就是有MissingSchemaAction来管理的。
MissingSchemaAction属性接受System.Data.MissingSchemaAction枚举类型的值。
默认值Add
可能值Ignore,表示忽略缺失列
可能值Error,表示有缺失列就产生异常。
可能值AddWithKey,表示有缺失列时,会在DataSet中添加缺失列,并且,设置MissingSchemaAction属性的两个附加属性:MaxLength和AllowDBNull。如果DataTable不存在或者DataTable不包含任何列,还会导致DataAdapter向数据库查找有关的主键信息。
2.7.2 FillSchema方法
此方法将架构信息存入DataSet或DataTable中.
此方法可以使用的参数:DataSet,DataTable或DataSet,表名称。
另外一个需要的参数是:SchemaType类型的值,取值Mapped或Source。(其中Mapped值将使DataAdapter使用TableMappings表映射的设置来应用返回列;而Source值则让DataAdapter使用查询结果来应用返回列)
另外,FillSchema方法会给返回列设置自增量(AutoIncrement)、允许空(AllowDBNull)、最大长度(MaxLength)等属性。
如果数据库中某列或某组列表示主键(或唯一键),FillSchema方法还要在结果DataTable上创建主键。