65.9K
CodeProject 正在变化。 阅读更多。
Home

通过 ADO.NET 和 XML 以编程方式使用

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.85/5 (86投票s)

2004 年 9 月 16 日

7分钟阅读

viewsIcon

351411

downloadIcon

5566

使用 ADO.NET,数据库可以被视为一个虚拟的 XML 文档。XPath 查询和 XSLT 转换自然可用。

引言

ADO.NET 可用作强大的 XML 中间件。尽管源自 ADO,但它已为更好地支持 Web 应用程序进行了彻底重新设计。ADO.NET 的一个主要特性是其断开连接的记录集,称为 DataSet。它充当功能丰富的内存数据库或数据缓存。换句话说,它不维护任何数据库服务器游标。DataSet 中提供了记录集的所有数据库特性,例如排序、分页、过滤视图、关系、索引以及主键/外键。

对 XML 的原生支持是 ADO.NET 的另一个主要特性。在原生模式下,记录表存储为 XML 文档,其中架构和数据被视为独立且可替换的元素。这与 ADO 有很大不同。ADO 的记录集原生存储格式是 Advanced Data Table Gram (ADTG) 文件格式,这是一种专有的二进制架构,表示记录集的内存映像。ADO.NET 中的 DataSet 对象可以从各种来源加载其数据:数据库服务器、本地或远程 XML 文件以及任何可访问的系统资源。一旦数据加载,它们就可以被视为记录表或虚拟 XML 文档。我们主要讨论后者。现在,我将为您提供几个用例。每个用例都是一个独立的单元测试,可以在 NUnit 下运行。因此,您应该安装最新版本的 NUnit。有关其安装和使用的更多信息,请参见 此处

在执行这些示例之前,您还应按照以下步骤设置数据库的 ODBC 连接

  1. 解压缩下载包。在“UnitTest_ADO.NET_XML”文件夹中有一个 Access 文件“NWIND_2002.MDB”。
  2. 打开控制面板 -> 管理工具 -> 数据源。
  3. 选择“系统数据源”选项卡。
  4. 单击“添加”按钮,然后从列表中选择“Microsoft Access Driver (*.mdb)”。然后会出现一个 ODBC 配置对话框。
  5. 将“XmlDb_NorthWind”输入为数据源名称 (DSN),并将 Access 文件“NWIND_2002.MDB”选作数据源。

以下是 Access 文件中表之间的关系

图 1:表之间的关系

将单个表转换为 XML 文档

使用 ADO.NET,我们可以轻松地在记录集表之上构建一个虚拟 XML 文档。其根元素具有与 DataSet 对象完全相同的名称。DataSet 对象中的表将作为根元素的子节点出现。例如,有一个名为“XmlDb”的 DataSet。当它转换为 XML 文档时,其根元素的名称将是“XmlDb”(参见图 2)。“Customers”表中的所有行都将映射到根元素的唯一子节点,其节点名称与表名“Customers”匹配。在图 2 中,“CustomID”、“CompanyName”等是“Customers”表的字段。当它们转换为 XML 时,它们将作为“Customers”的子节点出现。它们的节点名称将与字段名称相同,节点值将是该字段的值。

图 2:将单个表转换为 XML 文档。

以下简单示例解释了 ADO.NET 如何将 DataSet 对象映射到虚拟 XML 文档

using System;
using System.IO;
using System.Xml;
using System.Data;
using System.Data.Odbc;
using NUnit.Framework;
[Test] public void SingleTable2XML() 
{
//Create an ODBC connection to the database. Here it is an Access file
OdbcConnection conn = new OdbcConnection("DSN=XmlDb_NorthWind");

//Create a DataSet with a name "XmlDb"
DataSet dataset = new DataSet("XmlDb");

//Create a DataAdapter to load data from original data source to the DataSet
OdbcDataAdapter adapter = new OdbcDataAdapter();
adapter.SelectCommand = new OdbcCommand("SELECT * FROM Customers", conn);
adapter.Fill(dataset, "Customers");

//Create a virtual XML document on top of the DataSet
XmlDataDocument doc = new XmlDataDocument(dataset); 

//Output this XML document
doc.Save(Console.Out);

//NUnit test to confirm the result is exactly what we expect
Assert.AreEqual("XmlDb", doc.DocumentElement.LocalName);
Assert.AreEqual("Customers", doc.DocumentElement.FirstChild.LocalName);
}

输出

<?xml version="1.0" encoding="Windows-1252"?>
<XmlDb>
  <Customers>
    <CustomerID>ALFKI</CustomerID>
    <CompanyName>Alfreds Futterkiste</CompanyName>
    <ContactName>Maria Anders</ContactName>
    <ContactTitle>Sales Representative</ContactTitle>
    <Address>Obere Str. 57</Address>
    <City>Berlin</City>
    <PostalCode>12209</PostalCode>
    <Country>Germany</Country>
    <Phone>030-0074321</Phone>
    <Fax>030-0076545</Fax>
  </Customers>
   ……
</XmlDb>

将主详细信息表转换为 XML 文档

实体-关系模型是一种经过长期检验的将现实世界映射到数据库结构的方法。关系通常表示为主详细信息表,这些表可以自然地转换为 XML 父/子节点,使用 ADO.NET。图 3 显示了这种映射

图 3:将主详细信息表转换为 XML 文档。

生成嵌套 XML 节点关键在于设置 DataRelation 对象,使用主键和外键将主表和详细信息表链接起来。就像这样

DataColumn primarykey = dataset.Tables["Customers"].Columns["CustomerID"];
DataColumn foreignkey = dataset.Tables["Orders"].Columns["CustomerID"];
DataRelation relation = dataset.Relations.Add(primarykey, foreignkey);

这还不够。您还必须将 DataRelation 的“Nested”属性设置为“true

relation.Nested = true;

完整代码

using System;
using System.IO;
using System.Xml;
using System.Data;
using System.Data.Odbc;
using NUnit.Framework;
[Test] public void MasterDetailTables2XML() 
{
//Create an ODBC connection to the database. Here it is an Access file
OdbcConnection conn = new OdbcConnection("DSN=XmlDb_NorthWind");

//Create a DataSet with a name "XmlDb"
DataSet dataset = new DataSet("XmlDb");

//Load master table from original data source to the DataSet
OdbcDataAdapter adapter = new OdbcDataAdapter();
adapter.SelectCommand = new OdbcCommand("SELECT * FROM Customers", conn);
adapter.Fill(dataset, "Customers");

//Load detail table from original data source to the DataSet
adapter.SelectCommand = new OdbcCommand("SELECT * FROM Orders", conn);
adapter.Fill(dataset, "Orders");

//Get the primary key column from the master table
DataColumn primarykey = dataset.Tables["Customers"].Columns["CustomerID"];

//Get the foreign key column from the detail table
DataColumn foreignkey = dataset.Tables["Orders"].Columns["CustomerID"];

//Assign a relation
DataRelation relation = dataset.Relations.Add(primarykey, foreignkey);

//Ask ADO.NET to generate nested XML nodes
relation.Nested = true;

//Create a virtual XML document on top of the DataSet
XmlDataDocument doc = new XmlDataDocument(dataset); 

//Output this XML document
doc.Save(Console.Out);

//NUnit test to confirm the result is exactly what we expect
Assert.AreEqual("XmlDb", doc.DocumentElement.LocalName);
Assert.AreEqual("Customers", doc.DocumentElement.FirstChild.LocalName);
Assert.AreEqual("Customers", 
           doc.GetElementsByTagName("Orders")[0].ParentNode.LocalName);
}

输出

<XmlDb>
  <Customers>
    <CustomerID>ALFKI</CustomerID>
    <CompanyName>Alfreds Futterkiste</CompanyName>
    <ContactName>Maria Anders</ContactName>
    <ContactTitle>Sales Representative</ContactTitle>
    <Address>Obere Str. 57</Address>
    <City>Berlin</City>
    <PostalCode>12209</PostalCode>
    <Country>Germany</Country>
    <Phone>030-0074321</Phone>
    <Fax>030-0076545</Fax>
    <Orders>
      <OrderID>10643</OrderID>
      <CustomerID>ALFKI</CustomerID>
      <OrderDate>1995-09-25T00:00:00.0000000+02:00</OrderDate>
      <RequiredDate>1995-10-23T00:00:00.0000000+02:00</RequiredDate>
      <ShippedDate>1995-10-03T00:00:00.0000000+02:00</ShippedDate>
      <Freight>29.4600</Freight>
      <ShipName>Alfreds Futterkiste</ShipName>
      <ShipAddress>Obere Str. 57</ShipAddress>
      <ShipCity>Berlin</ShipCity>
      <ShipPostalCode>12209</ShipPostalCode>
      <ShipCountry>Germany</ShipCountry>
    </Orders>
    <Orders>
      <OrderID>10692</OrderID>
      <CustomerID>ALFKI</CustomerID>
      <OrderDate>1995-11-03T00:00:00.0000000+01:00</OrderDate>
      <RequiredDate>1995-12-01T00:00:00.0000000+01:00</RequiredDate>
      <ShippedDate>1995-11-13T00:00:00.0000000+01:00</ShippedDate>
      <Freight>61.0200</Freight>
      <ShipName>Alfred's Futterkiste</ShipName>
      <ShipAddress>Obere Str. 57</ShipAddress>
      <ShipCity>Berlin</ShipCity>
      <ShipPostalCode>12209</ShipPostalCode>
      <ShipCountry>Germany</ShipCountry>
    </Orders>
  </Customers>
   ……
</XmlDb>

使用 XPath 查询数据库

.NET Framework 在其 System.Xml 命名空间中实现了所有 DOM 接口。此外,它还集成了 XPath,作为 DOM 的扩展,在 XmlNode 级别上。因此,一旦构建了虚拟 XML 文档,就可以立即使用 XPath 进行查询。例如,在上一节中,我们创建了一个代表“Customers”及其相应“Orders”的 XML 文档。现在,我们想找出所有位于柏林的客户,并且要求将订购的产品运往德国。我们可以在 XML 文档上用一行代码执行此类搜索

XmlNodeList nodeList = 
    doc.SelectNodes("/XmlDb/Customers/Orders[../City='Berlin'" + 
                                 " and ShipCountry='Germany']");

它会将所有符合条件的节点放入一个 XmlNodeList 中,可以使用“foreach”进行访问

foreach (XmlNode node in nodeList)
{
......
}

这是一个完整的示例

using System;
using System.IO;
using System.Xml;
using System.Data;
using System.Data.Odbc;
using NUnit.Framework;
[Test] public void QueryWithXPath() 
{
//Create an ODBC connection to the database. Here it is an Access file
    OdbcConnection conn = new OdbcConnection("DSN=XmlDb_NorthWind");

    //Create a DataSet with a name "XmlDb"
    DataSet dataset = new DataSet("XmlDb");

    //Load master table from original data source to the DataSet
    OdbcDataAdapter adapter = new OdbcDataAdapter();
    adapter.SelectCommand = new OdbcCommand("SELECT * FROM Customers", conn);
    adapter.Fill(dataset, "Customers");

    //Load detail table from original data source to the DataSet
    adapter.SelectCommand = new OdbcCommand("SELECT * FROM Orders", conn);
    adapter.Fill(dataset, "Orders");

    //Get the primary key column from the master table
    DataColumn primarykey = dataset.Tables["Customers"].Columns["CustomerID"];

    //Get the foreign key column from the detail table
    DataColumn foreignkey = dataset.Tables["Orders"].Columns["CustomerID"];

    //Assign a relation
    DataRelation relation = dataset.Relations.Add(primarykey, foreignkey);

    //Ask ADO.NET to generate nested XML nodes
    relation.Nested = true;

    //Create a virtual XML document on top of the DataSet
    XmlDataDocument doc = new XmlDataDocument(dataset); 

    //Create an output buffer
    StringBuilder stringBuilder = new StringBuilder();
    stringBuilder.Append("<RESULTS>");

    //Perform an XPath query
    XmlNodeList nodeList = 
       doc.SelectNodes("/XmlDb/Customers/Orders[../City='Berlin'" + 
                                    " and ShipCountry='Germany']");

    //Visit results in the list
    foreach (XmlNode node in nodeList)
    {
        stringBuilder.Append(node.OuterXml);

        //NUnit tests to confirm the result is exactly what we expect
        Assert.AreEqual("ShipCountry", node.ChildNodes[10].LocalName);
        Assert.AreEqual("Germany", node.ChildNodes[10].InnerText);
        Assert.AreEqual("City", node.ParentNode.ChildNodes[5].LocalName);
        Assert.AreEqual("Berlin", node.ParentNode.ChildNodes[5].InnerText);
    }
    stringBuilder.Append("</RESULTS>");
    XmlDocument docResult = new XmlDocument();
    docResult.LoadXml(stringBuilder.ToString());
    docResult.Save(Console.Out);
}

输出

<?xml version="1.0" encoding="Windows-1252"?>
<Results>
  <Orders>
    <OrderID>10643</OrderID>
    <CustomerID>ALFKI</CustomerID>
    <OrderDate>1995-09-25T00:00:00.0000000+02:00</OrderDate>
    <RequiredDate>1995-10-23T00:00:00.0000000+02:00</RequiredDate>
    <ShippedDate>1995-10-03T00:00:00.0000000+02:00</ShippedDate>
    <Freight>29.4600</Freight>
    <ShipName>Alfreds Futterkiste</ShipName>
    <ShipAddress>Obere Str. 57</ShipAddress>
    <ShipCity>Berlin</ShipCity>
    <ShipPostalCode>12209</ShipPostalCode>
    <ShipCountry>Germany</ShipCountry>
  </Orders>
  <Orders>
    <OrderID>10692</OrderID>
    <CustomerID>ALFKI</CustomerID>
    <OrderDate>1995-11-03T00:00:00.0000000+01:00</OrderDate>
    <RequiredDate>1995-12-01T00:00:00.0000000+01:00</RequiredDate>
    <ShippedDate>1995-11-13T00:00:00.0000000+01:00</ShippedDate>
    <Freight>61.0200</Freight>
    <ShipName>Alfred's Futterkiste</ShipName>
    <ShipAddress>Obere Str. 57</ShipAddress>
    <ShipCity>Berlin</ShipCity>
    <ShipPostalCode>12209</ShipPostalCode>
    <ShipCountry>Germany</ShipCountry>
  </Orders>
  ......
</Results>

XmlElement 和 DataRow 之间的映射

尽管使用 XML 访问 DataSet 提供了一些独特的优势,但我们仍然需要获取有关数据的补充信息。例如,ADO.NET 用其当前状态标记每个 DataRow,即 AddedDeletedDetachedModifiedUnchanged。这些状态在我们执行对数据源的更新时很重要。幸运的是,XmlDataDocument 提供了一个有用的方法来帮助我们从 XmlElement 获取相应的 DataRow。一旦我们获取了 DataRow,就可以通过其 RowState 属性获得其当前状态

DataRow row = xmlDataDocument.GetRowFromElement(xmlElement);
Console.Write("RowState: ");
switch(row.RowState)
{
    case DataRowState.Added:
        Console.WriteLine("Added");break;
    case DataRowState.Deleted:
        Console.WriteLine("Deleted");break;
    case DataRowState.Detached:
        Console.WriteLine("Detached");break;
    case DataRowState.Modified:
        Console.WriteLine("Modified");break;
    default:
        Console.WriteLine("Unchanged");break;
}

我们还可以检测更新后的错误

DataRow row = xmlDataDocument.GetRowFromElement(xmlElement);
if (row.HasErrors)
    Console.WriteLine(row.RowError);
else
    Console.WriteLine("Everything is OK.");

XmlDataDocument 提供了另一个方法 GetElementFromRow 来将 DataRow 映射到 XmlElement。这是一个完整的示例

using System;
using System.IO;
using System.Xml;
using System.Data;
using System.Data.Odbc;
using NUnit.Framework;
[Test] public void MappingBetweenXmlElementAndDataRow() 
{
    //Create an ODBC connection to the database. Here it is an Access file
    OdbcConnection conn = new OdbcConnection("DSN=XmlDb_NorthWind");

    //Create a DataSet with a name "XmlDb"
    DataSet dataset = new DataSet("XmlDb");

    //Create a DataAdapter to load data from 
    //original data source to the DataSet
    OdbcDataAdapter adapter = new OdbcDataAdapter();
    adapter.SelectCommand = new OdbcCommand("SELECT * FROM Products", conn);
    adapter.Fill(dataset, "Products");

    //Create a virtual XML document on top of the DataSet
    XmlDataDocument doc = new XmlDataDocument(dataset); 

    Console.WriteLine("=========== GetRowFromElement ================");

    //Perform XPath query
    XmlNodeList nodeList = doc.SelectNodes("/XmlDb/Products[CategoryID=3]");
    foreach (XmlNode node in nodeList)
    {
        //Map XmlElement to DataRow
        DataRow row = doc.GetRowFromElement((XmlElement) node);
        Console.WriteLine("Product Name = " + row["ProductName"]);
        Assert.AreEqual(3, row["CategoryID"]);
    }

    Console.WriteLine("=========== GetElementFromRow ================");

    //Perform ADO.NET native query
    DataRow[] rows = dataset.Tables["Products"].Select("CategoryID=3");
    foreach (DataRow row in rows)
    {
        //Map DataRow to XmlElement
        XmlElement elem = doc.GetElementFromRow(row);
        Console.WriteLine("Product Name = " + elem.ChildNodes[1].InnerText);
        Assert.AreEqual("3", elem.ChildNodes[2].InnerText);
    }
}

输出

=========== GetRowFromElement ================
Product Name = Pavlova
Product Name = Teatime Chocolate Biscuits
Product Name = Sir Rodney's Marmalade
Product Name = Sir Rodney's Scones
Product Name = NuNuCa Nuß-Nougat-Creme
Product Name = Gumbär Gummibärchen
Product Name = Schoggi Schokolade
Product Name = Zaanse koeken
Product Name = Chocolade
Product Name = Maxilaku
Product Name = Valkoinen suklaa
Product Name = Tarte au sucre
Product Name = Scottish Longbreads
=========== GetElementFromRow ================
Product Name = Pavlova
Product Name = Teatime Chocolate Biscuits
Product Name = Sir Rodney's Marmalade
Product Name = Sir Rodney's Scones
Product Name = NuNuCa Nuß-Nougat-Creme
Product Name = Gumbär Gummibärchen
Product Name = Schoggi Schokolade
Product Name = Zaanse koeken
Product Name = Chocolade
Product Name = Maxilaku
Product Name = Valkoinen suklaa
Product Name = Tarte au sucre
Product Name = Scottish Longbreads

直接使用 XSLT 从 DataSet 生成 HTML

DataSet 中的内容转换为各种输出格式,例如 HTML/XHTML、WAP、PDF、SVG 等,是相当有吸引力的。.NET Framework 为 XSLT 提供了完美的[, ]支持,这有助于此类转换。有关 XSLT 的更多信息,请参见 此处

现在,假设我们想生成一份关于最昂贵的十种产品订单详情的简单报告。按照前面的几节,我们可以轻松地从“Products”和“OrderDetails”表构建一个虚拟嵌套 XML 文档。然后,我们创建一个 XSTL 文件,如下所示

<?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="/">
<HTML>
  <BODY>
    <TABLE BORDER="0" cellspacing="3" cellpadding="8">
      <TR bgcolor="#FFCC00">
        <TD>Product Name</TD>
        <TD>Price</TD>
        <TD>Discount</TD>
        <TD>Quantity</TD>
        <TD>Total</TD>
      </TR>
      <xsl:apply-templates select="XmlDb/Products[position() < 10]">
        <xsl:sort select="UnitPrice" order="descending" data-type = "number" />
      </xsl:apply-templates>      
    </TABLE>
</BODY>
</HTML>
</xsl:template>
<xsl:decimal-format name="us" decimal-separator='.' grouping-separator=',' />
<xsl:template match="XmlDb/Products[position() < 10]">
<TR >
<xsl:if test="position() mod 2 = 0">
      <xsl:attribute name="bgcolor">#EEEEEE</xsl:attribute>
</xsl:if>
<xsl:if test="position() mod 2 = 1">
      <xsl:attribute name="bgcolor">#AAAAAA</xsl:attribute>
</xsl:if>
        <TD><xsl:value-of select="ProductName"/></TD>
        <TD><xsl:value-of select="format-number(UnitPrice, 
                                    '#.00', 'us')"/>$</TD>
<xsl:if test="number(OrderDetails/Discount) != 0">
        <TD>-<xsl:value-of select="number(OrderDetails/Discount)*100"/>%</TD>
</xsl:if>
<xsl:if test="number(OrderDetails/Discount) = 0">
        <TD>-----</TD>
</xsl:if>
       <TD><xsl:value-of select="OrderDetails/Quantity"/></TD>
        <TD><xsl:value-of 
              select="number(UnitPrice)*number(OrderDetails/Quantity)*
                      (1-number(OrderDetails/Discount))"/>$
        </TD>
      </TR>
      </xsl:template>
</xsl:stylesheet>

现在,编写一个简单的函数来执行 XSLT 转换

[Test] public void GenerateHTMLFromXSLT() 
{
//Create an ODBC connection to the database. Here it is an Access file
    OdbcConnection conn = new OdbcConnection("DSN=XmlDb_NorthWind");

    //Create a DataSet with a name "XmlDb"
    DataSet dataset = new DataSet("XmlDb");

    //Load "Products" table from original data source to the DataSet
    OdbcDataAdapter adapter = new OdbcDataAdapter();
    adapter.SelectCommand = new OdbcCommand("SELECT * FROM Products", conn);
    adapter.Fill(dataset, "Products");

    //Load "Order Details" table from original data source to the DataSet
    adapter.SelectCommand = new OdbcCommand("SELECT * FROM [Order Details]", conn);
    adapter.Fill(dataset, "OrderDetails");

    //Create a relationship between the two tables
    dataset.Relations.Add(
                dataset.Tables["Products"].Columns["ProductID"],
                dataset.Tables["OrderDetails"].Columns["ProductID"]).Nested = true;;

    //Build a virtual XML document on top of the DataSet
    XmlDataDocument doc = new XmlDataDocument(dataset); 

    //Load the XSLT file. NOTE: Here it is compiled as an embedded resource file
    Assembly assembly = System.Reflection.Assembly.GetExecutingAssembly();
    XslTransform xslTran = new XslTransform();
    Stream xslStream = 
      assembly.GetManifestResourceStream("UnitTest_ADO.NET_XML.Test.xslt");
    XmlTextReader reader = new XmlTextReader(xslStream);
    xslTran.Load(reader, null, null);
            
    //Output the result a HTML file
    XmlTextWriter writer = new XmlTextWriter("xsltresult.html", 
                                      System.Text.Encoding.UTF8);
    xslTran.Transform(doc.CreateNavigator(), null, writer, null);
    writer.Close();
}

输出

图 4:HTML 输出

扩展:高级 XPath 查询

标准的 XPath 作为数据库查询语言不够强大,例如:它缺少 DateTime 相关的函数。幸运的是,它可以与自定义 XPath 函数一起增强。Prajakta Joshi 先生在 MSDN 上发表了一篇非常全面的关于此主题的 文章。但他的方法太复杂,难以遵循。您必须手动为每个自定义函数指定函数名、参数数量及其类型、返回类型等。添加新的 XPath 函数不够灵活,并且难以维护。现在,让我们使用 .NET Framework 中的反射机制来简化此过程(参见图 5)。

图 5:自定义 XPath 函数

所有自定义函数只能作为静态方法出现在 XmlDbXPathFunctions 类中。一旦 XPathExpress 需要 XsltContextResolveFunction()XsltContext 创建一个 XmlDbXPathFunctionWrapper 对象,该对象实现了 IXsltContextFunction 接口。函数名和必需的参数类型将传递给其构造函数。在构造函数中,XmlDbXPathFunctionWrapper 尝试在 XmlDbXPathFunctions 类中查找最佳匹配的静态方法。然后 XPathExpression 调用 XmlDbXPathFunctionWrapperInvoke() 方法,该方法将调用 XmlDbXPathFunctions 类中实际对应的​​方法并返回结果。

这是一种非常灵活的扩展 XPath 的方法。如果您想将自己的函数添加到 XPath 执行上下文中,只需在 XmlDbXPathFunctions 类中编写一个静态方法即可。新函数将自动检测到。

已经添加了一些有用的 DateTime 函数。现在您可以从 DateTime XmlNode 中提取年、月、日、时、分、秒、滴答。当您尝试使用 DateTime 信息过滤 XmlNode 集时,这将非常有用。例如:"//Orders[ex:year(string(ShippedDate)) = 1995 and ex:month(string(ShippedDate)) <= 3]" 将查找 1995 年第一季度发货的所有订单。

[Test] public void XPathExtension()
{
    //Create an ODBC connection to the database. Here it is an Access file
    OdbcConnection conn = new OdbcConnection("DSN=XmlDb_NorthWind");

    //Create a DataSet with a name "XmlDb"
    DataSet dataset = new DataSet("XmlDb");

    //Create a DataAdapter to load data from original data source to the DataSet
    OdbcDataAdapter adapter = new OdbcDataAdapter();
    adapter.SelectCommand = new OdbcCommand("SELECT * FROM Orders", conn);
    adapter.Fill(dataset, "Orders");

    //Create a virtual XML document on top of the DataSet
    XmlDataDocument doc = new XmlDataDocument(dataset); 

    //Create an XPath navigator
    XPathNavigator nav = doc.CreateNavigator();

    //XPath expression
    String xpath = 
      "//Orders[ex:year(ShippedDate)=1995 and ex:month(ShippedDate)<=3]";

    //Compile the XPath expression
    XPathExpression xpathexp = nav.Compile(xpath);

    //Assign a customized XPath context
    XmlDbXPathContext context = new XmlDbXPathContext(new NameTable());
    context.AddNamespace("ex", "http://openvue.net");
    xpathexp.SetContext(context);

    //Perform XPath query
    XPathNodeIterator it = nav.Select(xpathexp);

    //Output the result
    StringBuilder stringBuilder = new StringBuilder();
    stringBuilder.Append("<Results>");

    while (it.MoveNext())
    {
        XmlElement elem = (XmlElement)((IHasXmlNode)it.Current).GetNode();
        stringBuilder.Append(elem.ChildNodes[4].OuterXml);
        DateTime dt = Convert.ToDateTime(elem.ChildNodes[4].InnerText);
        Assert.AreEqual(1995, dt.Year);
        Assert.IsTrue(dt.Month <= 3);
    }
    stringBuilder.Append("</Results>");
    XmlDocument docResult = new XmlDocument();
    docResult.LoadXml(stringBuilder.ToString());
    docResult.Save(Console.Out);
}

输出

<?xml version="1.0" encoding="Windows-1252"?>
<Results>
  <ShippedDate>1995-01-03T00:00:00.0000000+01:00</ShippedDate>
  <ShippedDate>1995-01-02T00:00:00.0000000+01:00</ShippedDate>
  <ShippedDate>1995-02-16T00:00:00.0000000+01:00</ShippedDate>
  <ShippedDate>1995-01-13T00:00:00.0000000+01:00</ShippedDate>
  <ShippedDate>1995-01-16T00:00:00.0000000+01:00</ShippedDate>
  <ShippedDate>1995-02-10T00:00:00.0000000+01:00</ShippedDate>
  <ShippedDate>1995-02-10T00:00:00.0000000+01:00</ShippedDate>
  <ShippedDate>1995-02-09T00:00:00.0000000+01:00</ShippedDate>
  <ShippedDate>1995-03-02T00:00:00.0000000+01:00</ShippedDate>
  <ShippedDate>1995-02-27T00:00:00.0000000+01:00</ShippedDate>
  <ShippedDate>1995-02-27T00:00:00.0000000+01:00</ShippedDate>
  <ShippedDate>1995-03-03T00:00:00.0000000+01:00</ShippedDate>
  <ShippedDate>1995-03-27T00:00:00.0000000+02:00</ShippedDate>
  <ShippedDate>1995-02-27T00:00:00.0000000+01:00</ShippedDate>
  <ShippedDate>1995-03-17T00:00:00.0000000+01:00</ShippedDate>
  <ShippedDate>1995-03-09T00:00:00.0000000+01:00</ShippedDate>
<ShippedDate>1995-03-31T00:00:00.0000000+02:00</ShippedDate>
……
</Results>

如果您需要一些特殊的 XPath 函数,只需将它们作为静态方法添加到 XmlDbXPathExtensionFunctions 类中即可。这些静态方法将被自动找到并应用于您的 XPath 查询。真的很简单,不是吗?

结论

将数据库视为大型虚拟 XML 文档,使用 ADO.NET 并非总是明智之举。它需要更多的内存,并且在处理复杂且庞大的数据库时,性能可能是一个大问题。但对于相对较小的项目,它工作得很好,并且可能会为您节省大量时间。无论如何,您可以优化上述代码,使其满足您的要求。

© . All rights reserved.