利用 DataSetNavigator 在数据集上进行 XPath 查询(转载---来自MSDN)

利用 DataSetNavigator 在数据集上进行 XPath 查询

发布日期: 9/21/2004 | 更新日期: 9/21/2004

Arpan Desai

Microsoft Corporation

2004 年 8 月

适用于:

XML

DataSetNavigator

摘要:Arpan Desai 讨论了 DataSetNavigator,它提供 XML 编程模型的强大功能和灵活性,同时避免了将整个 DataSet 转换成 XmlDataDocument 对象的开销。

单击此处可下载本文的代码示例。

*
本页内容
简介简介
在数据集上构建 XPath Navigator在数据集上构建 XPath Navigator
DataSetNavigator 实现的详细信息DataSetNavigator 实现的详细信息
用法用法
小结小结

简介

一段时间以来,Dare 一直要求我为 MSDN 写一篇文章。既然 Microsoft Visual Studio 2005 Beta 1 已经(最终)发布了,我就抽出时间来写一下我这段时间以来的一些想法:DataSet 上的 XPathNavigator

XmlDataDocument 最初作为一个组件出现,它允许用户具有一个 Microsoft ADO.NET DataSet 数据的可编辑的层次视图。主要用例是在 DataSet 上执行 XSLT 转换的功能,以便生成用于在网页上表示的 HTML。遗憾的是,在 XSLT 处理过程中 XmlDataDocument 的性能通常都是一个瓶颈。

这个问题的一个解决方案就是利用 DataSet 上的 WriteXml() 方法并将序列化的内容加载到 XmlDocumentXPathDocument 类中。尽管使用这种方法会显著地提高性能,但是由于需要序列化和重新解析 XML 数据,所以它仍然不是最优的。此外,要进行转换或查询的数据现在同时驻留在 DataSetXmlDocument/XpathDocument 中,这意味着极大的增加内存使用。理想的解决方案应该具有本机 XML 存储区(例如 XPathDocument)的性能,同时占用最小的内存开销。

本文说明一个名为 DataSetNavigator 的类的实现,它尝试以理想的方式来解决这个问题。

在数据集上构建 XPath Navigator

XPathNavigator 是 XML 数据源上的只读光标。XML 光标就像每次聚焦于一个 XML 节点上的镜头,但是与基于推的 API(如 XmlReader)不同,该光标可以在任意给定时间内置于 XML 文档的任意位置。XPathNavigator 是用于在非 XML 数据上实现 XML 接口的优秀备选,因为它允许以实时方式构建数据源的 XML 视图,而不必将整个数据源转换成 XML 树。

通过适当的实现,您可以使用它来查询文件系统、Windows 注册表、Active Directory 存储区或任意其他类型的分层数据存储区。在以前的文章中,Steve Saxon 曾经使用 ObjectXPathNavigator 在对象图表上创建过一个 XPathNavigator

Microsoft .NET Framework 中的 XslTransform 类利用 XPathNavigator 在这些源上执行 XSLT 处理。此外,XPathNavigator API 还启用了 XPath 查询。通过实现 DataSetNavigator,我们可以在 DataSet 内容上同时执行 XSLT 转换和 XPath 查询。

一个必须要提出的问题是:为什么 DataSetNavigator 的实现会比 XmlDataDocument 速度快呢?XmlDataDocument 具有性能问题的一个根本原因在于它具有可编辑的能力。无论何时在 XmlDataDocument 的实例中发生更改,该更改就会传播到与它相关联的 DataSet。相反,对 DataSet 所做的任何更改也会同步附属的 XmlDataDocument。即使没有进行更改,这样设计的开销也会防止在查询或转换过程中的最优性能。在这两个方案中,不需要进行编辑;因此我们可以通过忽略同步功能的开销,从而创建更有效的 XPathNavigator 实现。

DataSetNavigator 实现的详细信息

目标相当简单:在 DataSet 顶端完成快速、轻量级的 XPathNavigator 实现。要实现的最复杂的功能就是理解 DataSet 中的嵌套关系,这样就可以借助于 DataSetNavigator 来表示层次结构。

实现 DataSetNavigator 的第一步就是理解如何使用 XPathNavigator 对基础数据源进行模型化。当不为 DataSet 提供 XML 架构时,由 XmlDataDocument WriteXml() 方法用于生成 XML 的规则就相当简单了。

DataSet 的名称为 XML 中的根元素。

不是嵌套关系中子元素的任意表中的行,均为根元素的子元素。

表的列均为每行元素中的子元素。

如果存在任意嵌套子行,这些行也均为该行元素的子元素。

从概念上讲,DataSetNavigator 的实现由两个主要部分组成。第一部分是基于 DataSet 的内容生成简单树的能力。第二部分是通过实现 XPathNavigator 导航该树的能力。在所提供的实现中,当用户实例化 DataSetNavigator 时会生成树,这会导致事先花费更多的开销来创建 DataSetNavigator,但在实际使用过程中开销会低得多。

树是从一系列节点中构建的,所有这些节点都派生自 DataSetNode 类。抽象 DataSetNode 类具有要求遍历所生成树的基本导航,而且具有几个非常重要的成员:

parent - 父成员指向当前节点的父级 DataSetNode

children - 子成员是 DataSetNodes 的数组,该 DataSetNodes 表示当前节点的子节点。

siblingPositionsiblingPosition 成员表示相对于其同级的当前节点位置的基于零的索引。这是必需的,因此在同级间的移动可以更简单有效的实现。

localNamelocalName 成员指向原子化的字符串,它是当前节点的本地名称。XPathNavigators 利用 XmlNameTable 对本地名称、命名空间前缀以及给定文档内的命名空间 URL 进行原子化和公开处理。这考虑到了在搜索这些项目时发生的低开销对象引用比较,而不是高开销的字符串相等比较。

我们实际的树是从特殊的类构建的,这些类派生自 DataSetNode。这些特殊的类对应于 DataSetNavigatorDataSet 中抽象出的不同类型的位置。基于上述的 XML 序列化规则,我们可以具有三种感兴趣的位置类型:

TopNode - 这是由位于 DataSet 上的 XPathNavigator 公开的顶级元素节点。

TableRowNode - 这种元素节点位于 DataSet 内的独立行上。

ColumnRowNode - 这种元素节点位于特定列、特定行上。

这三种节点类型涵盖了数据集中可用的不同位置。还有两种其他的 DataSetNode 派生类型,它们并不表示 DataSet 中的位置,但对于完成 DataSetNavigator 来说是必要的。

RootNode - 每个文档都具有根;这种节点是极其简单的节点类型,它表示文档的根。

CellValueNode - 我们需要一种表示驻留在 DataSet 中实际数据的方式。树中的每个 ColumnRowNode 都具有包含该数据的单一 CellValueNode 子节点。请注意,CellValueNode 的实现并不会导致对已经位于 DataSet 中的数据进行复制,而是返回了 DataSet 实例。

用法

DataSetNavigator 的用法类似于任何其他 XPathNavigator。DataSetNavigator 可以被传递到 XslTransform 类,以便进行 XSLT 处理,如下面的示例所示:

DataSet myDataSet = new DataSet();
...
DataSetNavigator myNavigator = new DataSetNavigator(myDataSet);
XslTransform myTransform = new XslTransform();
myTransform.Load("transform.xsl");
myTransform.Transform(myNavigator, null, Console.Out);

此外,可以针对 DataSet 执行 XPath 查询,如下面的示例所示:

using System; 
using System.Data;
using System.Xml; 
using System.Xml.XPath;
using Microsoft.Xml; 

public class DataSetNavTest{


  public static DataSet CreateDataSet(){

    DataSet custDS = new DataSet("Books");
    
    DataTable ordersTable = custDS.Tables.Add("Book");
    
    DataColumn pkCol = ordersTable.Columns.Add("BookID", typeof(Int32));
    ordersTable.Columns.Add("Title", typeof(string));
    ordersTable.Columns.Add("Quantity", typeof(int));
    ordersTable.Columns.Add("UnitPrice", typeof(decimal));
    ordersTable.Columns.Add("Category", typeof(string));
    
    ordersTable.PrimaryKey = new DataColumn[] {pkCol}; 
    
    ordersTable.Rows.Add(new Object[]{101,
     "Quantum Physics for Beginners", 2, 50, "Science"}); 
    ordersTable.Rows.Add(new Object[]{201, 
     "Repair your car with twine", 100, 24.99, "Automotive"}); 
    ordersTable.Rows.Add(new Object[]{301, 
  "The Secret of Life, The Universe and Everything", 42, 41.99, "Humor"}); 

    custDS.AcceptChanges(); 

    return custDS;
  }

  public static void Main(string[] args){

    DataSet ds = CreateDataSet(); 
    Console.WriteLine(ds.GetXml());

    DataSetNavigator nav = new DataSetNavigator(ds); 
    XPathNodeIterator iter = 
     nav.Select( "/Books/Book[(UnitPrice * Quantity) > 1000]" );

    while( iter.MoveNext() ){

   /* print title and total price of order using XPath queries*/ 
   string itemTotal = 
    iter.Current.Evaluate( "UnitPrice * Quantity").ToString();
   string itemTitle = 
    iter.Current.Evaluate("string(Title)").ToString();    

   Console.WriteLine("+{0} = {1}", itemTitle, itemTotal );

   /* print title and total price of order using DataSet APIs */ 
   DataSetNavigator nav2 = (DataSetNavigator) iter.Current.Clone(); 
   DataRow row           = nav2.GetDataRow(); 

   string itemTotal2 = 
   (((decimal)row["UnitPrice"]) * ((int)row["Quantity"])).ToString();
   string itemTitle2 = row["Title"].ToString();    

   Console.WriteLine("-{0} = {1}", itemTitle2, itemTotal2 );

      }
  }

}

DataSetNavigator 除了作为 XPathNavigator 外,还实现了 IDataSetPosition 接口。这种接口旨在返回当前 DataSetNavigator 所定位的 DataSet 中的位置,它考虑到需要访问 DataSet 的实际 DataRows/DataColumns 的高级方案。IDataSetPosition 公开了 GetPositionType() 方法,该方法返回当前状态的一个枚举值:

DataSetPosition.DataSet DataSetNavigator 位于 DataSet 上。调用 GetDataSet() 将会成功地返回当前 DataSet。调用 GetDataRow()GetDataColumnIndex() 将会失败,因为该 Navigator 没有位于特定行或列上。

DataSetPosition.RowDataSetNavigator 当前位于特定的 DataRow 上。调用 GetDataSet() 将返回当前 DataRow 作为其组成部分的 DataSet。调用 GetDataRow() 将返回当前的 DataRow。调用 GetDataColumnIndex() 将产生一个错误。

DataSetPosition.Cell - 这表示 DataSetNavigator 位于特定单元格上。单元格被视为单个 DataRow 和单个 DataColumn 的交集。调用 GetDataSet()GetDataRow()GetDataColumnIndex() 将会在此例中全部成功。

小结

在初步测试中,根据要进行转换的数据大小和样式表的复杂程度,DataSetNavigator 在任何位置都比在 XmlDataDocument 上运行相同的转换的速度要快 2-50 倍。由于不再发生重新解析 DataSet 的内容这一事实,DataSetNavigator 也比当前可用的 XmlDocumentXPathDocument 解决方案快 2-5 倍。

当前的 DataSetNavigator 相当地简单,并且没有集成任何有趣的功能。缺失的一项功能就是对命名空间的支持。当前的方法就是将 DataSet 转化成 XML 支持命名空间,以便在它们与各个 DataSet 部分相关联时进行序列化。当前的 DataSetNavigator 并没有提供这些命名空间。添加该功能将会相当简单,我将它作为感兴趣读者的一个练习。

转到原英文页面

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值