为ADO程序设计的ADO.NET(二)
转换现有代码
有许多 ASP 页面使用 ADO 对象来抽取数据。让我们来讨论几种典型的情况,您在不久的将来移植和改编代码时可能会遇上这些情形。
如果您有从单个记录集生成报表的 ASP 页面,DataReader 对象将是您最好的伙伴。
您浏览 DataReader 对象时,它会将结果输出到页面。String strConn, strCmd; strConn = "DATABASE=MyAgenda;SERVER=localhost;UID=sa;PWD=;"; strCmd = "Select * From Names where ID=" + contactID.Text; SQLConnection oCN = new SQLConnection(strConn); SQLCommand oCMD = new SQLCommand(strCmd, oCN); oCN.Open(); SQLDataReader dr; oCMD.Execute(out dr); while (dr.Read()) { // 使用 dr.GetString(index) 或 // dr["field name"] 的方法 Response.Write 来输出数据}
您还可以用 HasMoreRows 属性快速检查 DataReader 是否为空。如果您只需要快速浏览一系列记录,没有比 DataReader 更好更快的对象了。它同样适用于查询单个记录。您不能编辑 DataReader 的内容,但您可以将其内容移入更易于管理的对象,例如 DataTable 或者一个或多个 DataRow 对象。
当您需要处理表和记录之间的复杂关系时,DataReader 就不再是合适的工具了。在 ADO 中, 最终您需要处理记录集。您的数据模型链接越多,SQL 命令就越复杂。导航模型仍然是顺序的,最后放入缓存的数据往往多于你所需要的。DataSet 和 DataRelation 对象是这种表关系模型的基础。
为了管理父/子关系,ADO 还封装了数据形成引擎。从功能上讲,数据形成和 ADO.NET 关系是一样的。然而,从设计方面来看,它们几乎没有什么共同点。形成记录集将所有信息嵌入单个列表对象。ADO.NET 关系是您可以随时在两个数据表之间建立的动态链接。为了在执行单个 ADO 命令的过程中创建一个层次结构记录集,ADO 要依靠 Shaping OLE DB 服务提供程序,并且使用特定的类 SQL 语言。
在 ADO.NET 中,关系中涉及的每个对象总是被看成单独的个体。关系本身作为对象被公开,并且具有一定的行为规则。例如,DataRelation 对象可以从父行到子行一层层进行更改。您可以通过将 ForeignKeyConstraint 对象添加到 DataTable 的 Constraints 集合中来进行此操作。ForeignKeyConstraint 对象表示当删除或更新数值和行时,对通过外键关系相关联的一组列的约束。如前面提到的,一旦设置好了关系,在它按程序预设终止之前,您不能进行可能破坏该关系的更改。
另外,关系是不可传递的。您可以建立两组不同的关系,例如客户和订单、订单和产品之间的关系。然而,当在订单中导航以寻找某一位客户时,您不能从一个订单跳到与之相关的产品行。您必须另外打开订单/产品关系,定位到您需要的订单,然后才能获取相关的行。这就是为什么有时候最好不要通过原来的无格式 SQL JOIN 语句实现一对一关系的原因。
需要在 ASP Session 对象中存储记录吗?利用 ADO.NET 和 DataSet 对象,您可以相当安全的操作而不会导致在在 GIT 中存储 ADO 记录集可能会导致访问冲突(英文)中所讨论的问题,也不会有线程相似性的麻烦。更新数据
更新数据时,Web 应用程序通常使用无格式 SQL 语句,或者使用更好的参数化存储过程。然而,当需要使用未连接的数据时,您可能想使用内置服务来更新所有需要修订的记录。ADO 提供了批更新机制来实现这个功能。
UpdateBatch 方法用于把保存在副本缓冲中的 Recordset 更改发送到服务器,以更新数据源。它采用开放式锁定,允许所有挂起的本地更改。它还在单个操作中把所有更改传送到数据源。仅当更改提交后数据源锁定要更改的记录时,才会出现开放式锁定。开放式锁定使两个用户可以同时访问同一个记录,但一个用户输入的更改很快会被另一用户所覆盖。当然,这种方式要求数据源能够检测和防止数据冲突。还要求整个数据源比较稳定,不会发生频繁的更改。否则,不难想象协调费用将很快超过替代严格锁定所带来的节约。事实上,使用 UpdateBatch 方法,在任何更改失败时都会返回一个错误。然后,您可以通过 Errors 集合和 Error 对象来访问该错误。
要理解 ADO.NET 模型为什么是更新数据的更强大的工具,理解 ADO 中开放式锁定的工作原理是非常关键的。在 ADO 代码中,您无法控制调用 UpdateBatch 之后所发生的一切。也就是说,更新是在服务器上通过滚动已更改的行,然后比较原始值和数据源中对应记录中的当前值来进行的。当所有的值都一致了,才对表执行适当的 SQL 语句(INSERT、UPDATE 或 DELETE)。
问题在于您不能控制实际应用于更改的 SQL 语句。服务器端的更新代码并不比您编写的代码好,如果您采用非 SQL 提供程序,它甚至无法运行。在本节的开头,我曾讲过 Web 应用程序通常通过参数化存储过程来更新数据。然而,如果您使用批更新就不同了。
在 ADO.NET 中,这个模型已经有所扩展。现在它采用更通用的架构,允许您自己指定基本操作命令,例如插入、删除、更新和选择等。其用意很明显:不论何种数据源,都可以从中抽取数据并提供同样的支持。在 ADO.NET 中进行批更新,您需要创建 DataSetCommand 对象即 SQLDataSetCommand 或 ADODataSetCommand。
注意:在 Beta 2 中,DataSetCommand 对象将被称为 DataAdapter 对象。
拥有 DataSetCommand 对象之后,您便可以调用它的 Update 方法。DataSetCommand 提供 InsertCommand、DeleteCommand、UpdateCommand 和 SelectCommand 等属性。它们都是 Command 对象。但是,除非默认行为无法满足需要,否则您不必设置它们。这与在 ADO 中一样。在 Update 过程中,如果没有设置任何 xxxCommand 属性,但是存在主键信息,将自动生成 Command 对象。请注意,要使上述过程正确进行,必须为所涉及的数据表设置主键。
以下代码显示了如何为 DataSet 的 EmployeesList 表设置主键:DataColumn[] keys = new DataColumn[1];keys[0] = m_oDS.Tables["EmployeesList"].Columns["EmployeeID"]; m_oDS.Tables["EmployeesList"].PrimaryKey = keys;
主键基本上是 DataColumn 对象的一个数组。
如果您要使用存储过程来更新表,或者采用专用非 SQL 数据提供程序,您会经常用到这些命令属性。XML 扩展支持
在 ADO 中,XML 只不过是输入和输出格式。然而在 ADO.NET 中,XML 是一种数据格式,提供了操作、组织、共享和传递数据的手段。任何带入 DataSet 的数据,无论其来源,都能通过双面编程模型进行处理。您可以顺序交替访问信息,或者按行访问,也可以按照 XML 文档对象模型驱动的非顺序、层次结构路径进行访问。
DataSet 将数据和架构作为 XML 文档进行读写。数据和架构都可以通过 HTTP 传输,并且能在所有支持 XML 的平台上使用。相同的数据在不同的时候可以通过不同的架构来呈现,这是通过 XSLT 实现的。您可以使用 ReadXmlSchema 方法编写架构。XML 架构包括数据集中的表的说明,以及表的关系和约束。在调用 ReadXmlData 方法填充 DataSet 之前,应该先完成这个步骤。
以下代码示例是一个显示可更新数据表的最简单的 ASP.NET 页面。void Page_Load(Object source, EventArgs e){ DataSet data = new DataSet(); // 加载 XML 数据和架构 StreamReader sr; sr = new StreamReader(Server.MapPath("data.xml")); data.ReadXml(sr); sr.Close(); // 添加通过 URL 传递的新记录 if (Request.QueryString.Count >0) { DataTable dt = data.Tables[0]; DataRow dr = dt.NewRow(); dr["FirstName"] = Request.QueryString["First"]; dr["LastName"] = Request.QueryString["Last"]; dt.Rows.Add(dr); dt.AcceptChanges(); StreamWriter sw; sw = new StreamWriter(Server.MapPath("data.xml")); data.WriteXml(sw); sw.Close(); } // 刷新 UI(由网格组成) grid.DataSource = data.Tables[0].DefaultView; grid.DataBind();}
如图 2 所示,您可以将新的行添加到表中。然而,它不涉及 SQL Server 或 Access 表。它只是一个 XML 文件,在处理它的代码中,没有使用 XML 节点或 XMLDOM 方法。您可以用相同的直观数据表接口来读取和更新 XML 记录。您的工作方式与在 ADO 中大致相同,但此处的模型更深入、更庞大,有更多的潜力供您去发掘。
图 2:可更新表的示例总结
Web 应用程序的成功改变了典型分布式系统的面貌。现在大多数分布式系统都是 n 层系统,这类系统对扩展性和互操作性的要求越来越高。因此,非连接数据处理和 XML 成为最佳实践,并为业界广为接受。
ADO.NET 尝试将当今一些最好的实践统一在 .NET 下。这种用于数据访问的编程模型全面而又非常强大。但这个模型可能尚不能满足每一个人的要求,在将来的模型设计中还需要迈出一大步。然而,请记住现在 ADO.NET 还只是 Beta 版,只有有限的文档支持。
ADO 程序员从 Beta 版中获益最多,因为他们熟悉了 ADO.NET 的许多方面,包括最高层次的抽象即启发性模型。ADO.NET 代码与现有的 ADO 代码不兼容,但功能相似。要充分利用 ADO.NET,您应该花些功夫来理解概念本身,而不仅仅是找出移植代码的最快方式。无论您选择何种 .NET 编程模型,Windows 窗体、Web 窗体还是 Web 服务,ADO.NET 都会帮助您处理好数据访问的问题。