下载本文示例代码
传统的 ADO 和 ASP 均支持流处理。当然,在.NET中,Web services 同样支持流,并且 Web Services 提供了比传统的 ADO 和 ASP 多得多的功能。实际上,Web
Services 是一项处理数据请求并将数据以 XML 形式通过 HTTP 传输的技术。ADO 可以以流形式保存 XML,不过这是 ADO 后来的完善措施(afterthought)。而 ADO.NET 的 DataSet 对象通过 WriteXml 和
WriteXmlSchema 方法将其内容输出为一个 XML 文件或流。因此,ADO 的 Recordset 的 Save 方法是经过改进以适于将其内容输出为一个 XML,而 ADO.NET 的 DataSet
一开始就具有此特性。
ADO.NET 中不仅可以将 DataSet 保存为 XMl,并从 XML 加载 DataSet,并且它使用其类 XML 结构到别的用途中去。譬如,因为 DataSet 可以表示为 XML 形式,其可以很方便地在物理层和逻辑层间传递。这意味着,XML 可以在安全的网络上通过 HTTP 传送,同样
还可以以基于文本的 XML 形式传送。
由于构建在 XML 上,ADO.NET 可以工作在非连接状态。传统的 ADO Recordset 既可以工作在连接状态也工作在非连接状态,
取决于以下属性(譬如:CursorType
= adOpenStatic 和CursorLocation = adUseClient),而在 ADO.NET 中,RowSet 对象分为连接态的(DataReader)和非连接态(DataSet)。
连接
创建连接的过程在 ADO 和 ADO.NET 中非常相似。首先,声明你的连接对象,初始化,设置其连接字符串,打开它。如
Figure 1 所示。第一个例子演示如何使用 ASP 和 ADO 来打开一个连接,第二个例子则在 ASP.NET 和 ADO.NET 做同样的工作。
ADO 和 ADO.NET 建立连接的主要区别在于 ADO 使用一个 Connection 对象来完成所有的与各种数据源的连接,而 ADO.NET 用不同的连接对象来表示与不同数据源的连接。例如,ADO.NET 含有一个System.Data.SqlClient 命名空间,其中包含所有 SQL
Server 专用的 ADO.NET 对象(包括SqlConnection 对象)。SqlConnection 对象专门用于与 SQL Server 数据库进行通讯,因而是与 SQL
Server 交互最快,功能最丰富的对象。还有一个更通用的命名空间 System.Data.OleDb,它可以与所有兼容 OLE DB 数据源通讯。因此,在 ADO.NET 中,可以创建多个数据提供程序命名空间(data provider namespaces)以连接特定的数据源,使数据访问速度更快,效率更高,允许每个命名空间充分利用目标数据提供程序
的功能。如果某个程序在运行时必须改变数据提供程序的类型,或者依赖一个数据提供程序而该数据提供程序不含有特定的 ADO.NET 连接对象时,该程序最好使用 OleDbConnection。
Recordset 到 Readers
ADO 中的 Recordset,其行为(功能)依赖其属性设置的不同而不同,而在 ADO.NET 中 Recordset 被分解成许多不同的对象和方法。
从而避免了 Recordset 大而全的弊端,着眼于使各个对象各司其职,因此 ADO.NET 中的这种分解可以显著提高效率。ADO 中的 Recordset 可以是个连接的行集也可以是个非连接的行集。它可以充当一个前
向(forward-only)只读的游标,或者允许行集中位置的前移、后移和向中间移动。ADO 的 Recordset 允许对数据修改直接在数据库中完成,允许保存数据修改并以批处理形式发送给数据库。问题在于 ADO 的 Recordset 承担了太多的功能。ADO.NET 中将这些功能分解为多个对象去
分别完成特定的任务,因而 ADO Recordset 的功能被分为 DataSet 对象、DataReader 对象、DataAdapter 和 Command
对象。
前向、只读游标要求与数据源一直处于连接状态。ADO.NET 使用 DataReader 对象确保当其打开时,就一直处于连接状态。DataReader 专为
某一数据提供程序如 SQL
Server、Oracle 或更通用的 OLE DB 数据提供程序而编写。因此,SqlDataReader 对象可以连接到 SQL Server 数据库,并且充当一个 firehose
游标的角色,在大量的记录间循环。SqlDataReader 也具有对查询结果的快速前向(fast-forward-only)访问功能。它从数据库的查询结果中检索到一条记录,
并保持连接为打开状态,以便成功检索下一条记录。ADO.NET 的 DataReader 由于不支持 ADO Recordset 的所有功能,因而效率
极其高。Figure 2 中的例子演示了如何在传统的 ADO
和 ADO.NET 中实现一个只前向移动的游标的例子。值得注意的是在 ADO.NET 中,DataReader 对象的 Read 方法自动移动位置到下一条记录。这就避免了开发者在使用传统的 ADO
时,由于疏忽了调用ADO Recordset 的 MoveNext 方法而导致死循环的情况的出现。
ADO 和 ADO.NET 的另外一个区别是填充前向游标的方式。ADO 中所有的行集,无论前向的还是其它的,均包含在 Recordset 对象中。而 Recordset 对象通过 Recordset.Open 方法或者 Connection 或 Command 对象的 Execute 方法而打开。ADO.NET 中,
有一个专门的方法将获取前向数据提供给 DataReader:即 Command 对象的 ExecuteReader 方法,
该方法告诉命令对象为该数据提供程序明确提出 DataReader 对象以便以优化的前向方式处理结果。如前面例子所示。该命令对象还有一个方法叫 ExecuteXmlReader,
其作用是告诉命令对象生成查询结果供 XmlReader 对象处理。XmlReader 对象可被用于转换和处理 XML 查询结果,见下面的 ASP.NET 例子:
oCn.Open();
SqlCommand oCmd = new SqlCommand("SELECT * FROM Orders FOR XML AUTO",oCn);
oCmd.CommandType = CommandType.Text;
XmlReader oXR = oCmd.ExecuteXmlReader();
while(oXR.Read()) {
Response.Write(oXR.ReadOuterXml());
}
该例子演示了 XML 如何被发送到调用浏览器。不过,XML也可以整个被聚集和流化到 Response 对象。
单值命令
有时,大多数应用都需要从数据库查询中获取单个值的信息。在传统的 ADO 中标准的做法是创建一个 SQL 语句,并打开包含着查询结果的记录集。因为结果中只有一行一列,看起来似乎不必要。下列代码从查询中获取
单值:该例子获取 Orders 表中的行的数目:
''''-- ASP and ADO
Set oRs = Server.CreateObject("ADODB.Recordset")
oRs.ActiveConnection = oCn
oRs.Open "SELECT COUNT(*) As iRowCount FROM Orders"
iCount = oRs.Fields("iRowCount").Value
如果你只想取得仅仅一行和一列的信息,ADO.NET 中引入了从查询中获取单个值的新的方法。使用 ADO.NET 命令对象中的 ExecuteScalar 方法,
它可以返回其相关查询的第一行和列的信息。由于不必经过创建行集,查找该值,关闭行集等过程,因而开销非常之小。ExecuteScalar 方法在检索单值时是最佳的方法。下列代码和前例完成同样的任务,
但所使用的是 ASP.NET 和 ADO.NET 中 ExecuteScalar 方法:
string sSql = "SELECT COUNT(*) As iRowCount FROM Orders";
SqlCommand oCmd = new SqlCommand(sSql, oCn);
oCmd.CommandType = CommandType.Text;
int iCount = (int)oCmd.ExecuteScalar();
另外一种获取单值的方法是使用存储过程的输出参数。该技术也需要检索大量的值。在 ADO 和 ADO.NET 中均可采用,不同的是 ADO.NET 扩充了输出参数的功能。为了在 ADO.NET 中从一个命令对象中得到输出参数的值,使用 ExecuteNonQuery 方法执行查询语句。该方法告诉 ADO.NET,该查询不返回行集,因而可以避免产生 DataSet 或 DataReader 的
开销:
oCmd.ExecuteNonQuery();
oCmd.UpdatedRowSource = UpdateRowSource.OutputParameters;
int iOrderID = (int)oCmd.Parameters["@OrderID"].Value;
上述代码设置 UpdatedRowSource 属性以指向输出参数,前提是输出参数已被设置。然后就可以检索到输出的值。在传统的 ADO 中
,Connection 对象的 Execute 方法使用一个模糊的参数来完成以上任务,而 ADO.NET 是以一种直白的方式来实现的。当然,在 ADO.NET 中还有一种最佳的方法来返回一个标准的行集——即命令对象的 Execute 方法。
传统的 ADO 的 Recordset 对象能运用 UPDATE、INSERT、DELETE 等语句,这些语句对于用 Recordset
变化后的值修改底层数据库来说是必须的。尽管该功能很方便,但也还是带来许多开销,因为它必须返回到数据库去了解如何进行该操作。
ADO.NET 中通过 CommandBuilder 对象做到这一点;虽然它同样带来系统开销。在大多数开发情况下,具体的 SELECT、INSERT、UPDATE、DELETE 操作在设计时间就可以确定下来。在传统的 ADO 中,没有
一种方便的方法将查询行为与 Recordset 关联起来以便 Recordset 可以利用这些它们的优势。而在 ADO.NET 中,一个 DataAdapter 有四个不同的命令对象与之相关联,分别代表每个查询
动作和 SELECT 语句。这使得 DataAdapter 可以使用一个查询结果去填充一个 DataSet,并告诉DataAdapter 事先在数据库上采取哪些
查询动作。当然在设计阶段这需要更多的代码,不过这些额外的代码对于性能的提高是值得的,更不用说代码的易维护性了,因为这些代码是自解释的。
在我五月份的“Data Point”专栏里详细讨论了 DataAdapter 的使用方法。
本文中我讨论了从 ADO 到 ADO.NET 一些关键特性的演变,包括 firehost 游标和持续化
XML。在后续专栏里,我将探讨更多有关游标的演变并示范如何处理并发问题以及在传统 ADO 和 ADO.NET 中如何进行批量更新。
发送问题和建议给 John 的邮件地址:mmdata@microsoft.com
作者简介
John Papa 是个棒球迷,夏天大多数夜晚都与他的两个小女儿,妻子和忠实的狗 Kadi 一起为YanKees
队加油。他著有几本关于 ADO,XML 和 SQL Server 方面的书,常常能在 VSLive 这样的行业大会看到他演讲。
|
本文出自
MSDN Magazine 的
July 2004 期刊,可通过当地 报摊获得,或者最好是
订阅 |
下载本文示例代码