SQLXML初体验:用XML代替T-SQL来操作数据库

随着Internet的飞速发展,W3C成员意识到必须找到一种办法将数据和Web的表现方式分离出来,于是XML诞生了。当今的XML已经成为IT领域各个数据(特别是文档)的首选格式。由于它具有标记不同字段的能力,因此使搜索变得更简单。从微软发布SQL Server 2000的时候,就读XML数据的存储和检索提供内置的支持。而且微软早已意识到必须对其不断地改进,以便和不断发展的W3C的XML标准保持一致。在微软发布SQL Server 2000的几个月之后,它便在Web站点上发布了完全可以支持XML特性的软件包提供免费的下载。这些软件包被称作SQLXML(XML for SQL Server),当时的版本是3.0。5年之后,SQL Server 升级到了SQL Server 2005,提供了一系列的新的功能和特性,比如对新的XML数据类型的支持;提供了新的Data Access Provider——Native Client;等等。为了提供对这个新的功能的支持,和对原来版本的改进,SQLXML4.0被推出来。SQLXML4.0已经成为了一种成熟的数据访问技术。

我们怎么来看待SQLXML呢?它到底能为我们作些什么呢?我们可以把数据库中的数据和XML数据看成是同一数据的不同表现形式。如果能过在这两种数据表现形式之间提供一种Mapping,那么我们就可以实现这两种数据表现形式的转换。换句话说,我们就可以同样的数据从机遇关系数据库的存储形式,转变成标记语言的XML格式。而SQLXML就是实现了这样一种Mapping机制,并在此基础上提供机遇XML(而不是纯SQL)的数据操作方法。通过SQLXML,我们不但可以以XML的格式获取查询结果,我们还可以通过提过一个具有某种格式的XML实现数据库的添加,删除和修改;我们还可以提供一种有效的方式实现基于XML的数据批量上传。

在这里,我不打算做深入的介绍,只是通过提供一个简单的例子,是大家对SQLXML有一个感性的认识。如果大家有兴趣,我可以在后续的文章做详细的介绍。

我们的例子是这样的:在数据库库中,有两张表T_ORDER和T_ORDER_DETAIL,用于存储订单和订单明晰的信息。我们要做的是,通过SQLXML把相关数据已XML的形式取出,通过XSLT转化成HTML,从而生成我们的Web Page。所以这是一个简单的Web 应用。

表的结构,由于我们主要的目的在于介绍SQLXML,我们把业务逻辑和数据结构尽量精简。


注:这是我比较喜欢的一种数据表的设计方式:为每个表加上以下六个公共的字段——CREATED_BY,CREATED_ON,LAST_UPDATED_BY,LAST_UPDATED_ON,VERSION_NO,TRANSACTION_NO。前4个字段指明每条数据的建立和被最后写该得人的时间,有利于敏感数据的追踪和记录Log。VERSION_NO是一个Timestamp类型的字段,用于判断数据的并发。TRANSACTION_NO记录的书该记录的创建的更新属于某个原子事务,有利于进行Audit Log。就以上面这两个表为例,如果我们设计的数据非常敏感,我们需要有一个机制来记录每一次数据的创建和更新——操作时在什么时候,操作者是谁,原来的数据是什么,新的数据是什么。那么上面这样的结构可以为我们实现这样的功能。如果有机会,我们给大家详细的实现方式——我曾经为原来的公司做过相应的设计和实现,我觉得其设计理念的实现对于一个企业级别的应用来说还是有很高的价值的。

跑题了,我们把话题拉回来。为了大家能够对我们实现的功能有一个感性的认识,我现在把我们应用涉及的两个Web Page的Screen Shot先展示给大家。

Order.aspx: 列出所有的Order记录,Order No.为一个Link,通过它Redirect 到OrderDetail.aspx。


OrderDetail.aspx:列出当前Order的详细信息。

我们现在就开始来一步一步得来实现这个简单的应用。Source Code这里下载

1. 建立一个Website,下面的这个Website的结构。

  • Utility.cs: 提供一个Common的方法通过SQLXML从Database中查询数据。
  • Schema/Order.xsd: 这是一个被称为Mapping Schema的XSD。在SQLXML,Mapping Schema是最为重要的对象,因为所有基于SQLXML的操作都是建立在Database中的数据结构和XML有一个完全Mapping的基础上的,而这样的Mapping 就是通过Mapping Schema来实现的。
  • Template/Order_Sql.xml &Template/Order_Xpath.xml: SQLXML查询允许我们把查询的条件通过不同的方式传递到SqlXmlCommand(这个对象和ADO.NET忠德DbCommand有点相似,用于执行所有的Data Access 操作)——可以一纯字符串的形式;可以一Stream的形式;可以把它们保存在一个Tenplate文件中,通过这个文件传递。本例就是采用最后一种方式。在这个例子中,我们会以两种不同的方式来实现数据的查询——通过For XML Select语句和通过Mapping Schema 结合Xpath。
  • Transform/Order.xsl & Transform.OrderDetail.xsl:由于SQLXML获取的数据实际上是一个纯XML,但是我们希望把数据在Web Page中显示出来。所以我们需要通过这两个XSLT把XML转化成相应的HTML。
  • Order.css:应用于Web Page的Css,使得页面看上去相对好看一点。
  • Order.aspx & OrderDetail.aspx:Web Page。

2. 创建Mapping Schema

<?xml version="1.0" encoding="utf-16"?>
<xs:schema xmlns:sql="urn:schemas-microsoft-com:mapping-schema"     
       xmlns:xs
="http://www.w3.org/2001/XMLSchema">
  
<xs:annotation>
    
<xs:appinfo>
      
<sql:relationship name="order_orderdetail" parent="T_ORDER" parent-key="ORDER_ID" child="T_ORDER_DETAIL" child-key="ORDER_ID" >
      
</sql:relationship>
    
</xs:appinfo>
  
</xs:annotation>
  
<xs:element sql:relation="T_ORDER" name="order" type="orderType" />
  
<xs:complexType name="orderType">
    
<xs:sequence>
      
<xs:element sql:relation="T_ORDER_DETAIL" sql:relationship="order_orderdetail" name="product" type="productType" />
    
</xs:sequence>
    
<xs:attribute sql:field="ORDER_ID" name="id" type="xs:int" />
    
<xs:attribute sql:field="ORDER_DATE" name="date" type="xs:dateTime" />
    
<xs:attribute sql:field="SUPPLIER" name="supplier" type="xs:string" />
  
</xs:complexType>
  
<xs:complexType name="productType">
    
<xs:attribute sql:field="PRODUCT_ID" name="id" type="xs:int" />
    
<xs:attribute sql:field="PRODUCT_NAME" name="name" type="xs:string" />
    
<xs:attribute sql:field="PRODUCT_NAME" name="price" type="xs:double" />
    
<xs:attribute sql:field="QUANTITY" name="quantity" type="xs:int" />
  
</xs:complexType>
</xs:schema>

Mapping Schema是一个XSD,他实现了如何把数据库中的对象(比如一个表,一个字段,甚至表与表之间的关联)Mapping到XML中的某一格Element或者Attribute中。所有Mapping相关的Tag定义在这样一个Namespace中——urn:schemas-microsoft-com:mapping-schema。通过relationship实现了T_ORDER和T_ORDER_DETAIL之间的关联。通过relation把两个表Mapping到一order和product XML Element上,通过field把数据库中相关的字段Mapping到对应的XML Attribute上。仔细分析,上面的XSD实际上是定义了下面一种结构。

<?xml version='1.0' encoding="utf-8" ?>
<orders xmlns:sql="urn:schemas-microsoft-com:xml-sql">
  
<order id="1" date="2007-03-21T00:00:00" supplier="Dell Coporation">
    
<product id="1" name="PC" price="7000" quantity="25"/>
    
<product id="2" name="Laptop" price="13000" quantity="50"/>
  
</order>
  
<order id="2" date="2007-03-23T00:00:00" supplier="HP Coporation">
    
<product id="3" name="PC" price="8000" quantity="30"/>
    
<product id="4" name="Printer" price="3000" quantity="5"/>
  
</order>
  
<order id="3" date="2007-03-25T00:00:00" supplier="AA Coporation">
    
<product id="5" name="Pencil" price="0.4" quantity="3000"/>
  
</order>
</orders>

3. 创建用于查询的Template文件。

基于SQL的查询——Template/Order_Sql.xml

<?xml version="1.0" encoding="utf-8" ?>
<orders xmlns:sql="urn:schemas-microsoft-com:xml-sql">
  
<sql:header>
    
<sql:param name="orderID" >1</sql:param>
  
</sql:header>
  
<sql:query>
    SELECT    1 AS TAG,
    0 AS PARENT,
    dbo.T_ORDER.ORDER_ID AS [order!1!id],
    dbo.T_ORDER.ORDER_DATE AS [order!1!date],
    dbo.T_ORDER.SUPPLIER AS [order!1!supplier],
    NULL as [product!2!id],
    NULL as [product!2!name],
    NULL as [product!2!price],
    NULL as [product!2!quantity]
    FROM dbo.T_ORDER
    WHERE dbo.T_ORDER.ORDER_ID = @orderID OR @orderID =0
    UNION ALL

    SELECT
    2 AS TAG,
    1 AS PARENT,
    dbo.T_ORDER.ORDER_ID ,
    dbo.T_ORDER.ORDER_DATE,
    dbo.T_ORDER.SUPPLIER,
    dbo.T_ORDER_DETAIL.PRODUCT_ID,
    dbo.T_ORDER_DETAIL.PRODUCT_NAME,
    dbo.T_ORDER_DETAIL.UNIT_PRICE,
    dbo.T_ORDER_DETAIL.QUANTITY
    FROM
    dbo.T_ORDER INNER JOIN  dbo.T_ORDER_DETAIL
    ON dbo.T_ORDER.ORDER_ID = dbo.T_ORDER_DETAIL.ORDER_ID
    WHERE dbo.T_ORDER.ORDER_ID = @orderID OR @orderID =0
    ORDER BY [order!1!id],[product!2!id]
    FOR XML EXPLICIT
  
</sql:query>
</orders>

相信大家不会对这个感到陌生,我们通过在Select语句上运用FOR XML字句把原本已RowSet体现的结构转换成一个XML。仔细分析这个Select渔具,你会发现而通过 上的方式获得的结构是完全符合我们上面定义的Mapping Schema的。

接下来我们来通过第二种方式查询——Mapping Schema结合XPath的Template 文件:Template/Order_Xpath.xml。

<?xml version="1.0" encoding="utf-8" ?>
<orders xmlns:sql="urn:schemas-microsoft-com:xml-sql">
  
<sql:header>
    
<sql:param name="orderID" >1</sql:param>
  
</sql:header>
  
<sql:xpath-query mapping-schema="http://localhost/Artech.OrderManagement\Schema\Order.xsd">
    /order[@id=$orderID or $orderID = '0']
  
</sql:xpath-query>
</orders>

我们通过sql:header定义一个参数OrderID,相应的查询被置于 <sql:xpath-query〉中()注意上面基于SQL的查询对于的是<sql:query>。通过mapping-schema运用我们定义的Mapping Schema。通过一个XPath定义我们的查询条件——如果传入的参数时’0’)(虽然OrderID在DB中是Int,但是转化成XML,我们不能区分它到底是Int还是String,而一般地,XML 把它当成Sring处理)则返回所有Order,否则返回对应ID的Order。

其实我们我们可以这样来理解广义的查询——查询时把筛选条件运用于某个具有预先知道的结构的数据集而获得的复合你指定的筛选条件的数据集。首先查询是机遇某种结构的,举个例子,在电影院中,你可以通过你的电影票很快找到你所需要的座位,是因为电影院是按照排和列来安置作为的,同样我们可以通过标准的SQL的Where语句很快地获取我们需要的结果集,这是因为所有的关系型DBMS都是同一种形如矩阵的结构来存储数据的。我们可以通过XPath在XML中帅选我们希望的结果也是一样的道理。所以对于我们可以通过XPath来从DB中查询数据,便不会感到奇怪了——首先Mapping Schema通过Mapping机制实际上给我们提供了XML形式的DB,而XPath就像是XML DB中的Where子句一样。

4. 创建XSLT

我们已经提到SQLXML查询的结果只是一个单纯的XML,要 把他们置于一个Web Page,我们需要把他们转化成HTML,而且我们已经通过我们定义的Mapping Schema和SQL知道的将会生成的XML的Schema,要完成这样的功能,我们很自然地想到XSLT。 

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0"  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="orders">
  
<html xmlns="http://www.w3.org/1999/xhtml">
    
<head>
      
<title>Order Management</title>
      
<link href="Order.css" rel="stylesheet" type="text/css" />
    
</head>
    
<body>
      
<table cellpadding="0px" cellspacing="0px"  >
        
<tr>
          
<td class="heading2" >
            Order No.
          
</td>
          
<td class="heading2" >
            Date
          
</td>
          
<td class="heading2" >
            Supplier
          
</td>
        
</tr>
        
<xsl:apply-templates select="order"></xsl:apply-templates>
      
</table>
    
</body>
  
</html>
</xsl:template>
 
<xsl:template match="order">
    
<tr>
      
<td>
        
<xsl:element name="a">
          
<xsl:attribute name="href">
            OrderDetail.aspx?id=
<xsl:value-of select="@id"/>
          
</xsl:attribute>
          
<xsl:value-of select="@id"/>
        
</xsl:element>
      
</td>
      
<td>
        
<xsl:value-of select="@date"/>
      
</td>
      
<td>
        
<xsl:value-of select="@supplier"/>
      
</td>
    
</tr>
  
</xsl:template>
</xsl:stylesheet> 

Transform/OrderDetail.xsl

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="orders">
  
<html xmlns="http://www.w3.org/1999/xhtml">
    
<head>
      
<title>Order Management</title>
      
<link href="Order.css" rel="stylesheet" type="text/css" />
    
</head>
    
<body>
      
<xsl:apply-templates select="order"></xsl:apply-templates>
    
</body>
    
</html>
</xsl:template>
  
<xsl:template match="order">
    
<table cellpadding="0px" cellspacing="0px"  >
      
<tr>
        
<td colspan="2" class="heading2">
          Order No.:
<xsl:value-of select="@id"/>
          Date:
<xsl:value-of select="@date"/>
          Supplier:
<xsl:value-of select="@supplier"/>
        
</td>
      
</tr>
      
<xsl:apply-templates select="product"></xsl:apply-templates>
    
</table>
  
</xsl:template>
  
<xsl:template match="product">
    
<tr>
      
<td colspan="2">
        Product No.:
<xsl:value-of select="@id"/>
      
</td>
    
</tr>
    
<tr>
       
<td width="10%">Name</td>
    
<td style="background-color:#ECF9EE">
      
<xsl:value-of select="@name"/>
    
</td>
    
</tr>
    
<tr >
      
<td >Price</td>
      
<td style="background-color:#ECF9EE">
        
<xsl:value-of select="@price"/>
      
</td>
    
</tr>
    
<tr >
      
<td >Quantity</td>
      
<td style="background-color:#ECF9EE">
      
<xsl:value-of select="@quantity"/>
    
</td>
    
</tr>   
  
</xsl:template>
</xsl:stylesheet> 

5. 创建CSS

table
{    
    width
:100%;
    border
:solid 1px orange;
}


heading1
{
        font-size
:14px;
        font-weight
:bold;
}


.heading2
{
    font-size 
:12px;
    font-weight 
:bold;
    background-color
:#F2F1AE
}


td
{
    border
:solid 1px #CFA441;    
    background-color
:#BAE7C2;
    font-family
:Verdana;
    font-size
:10px;        
}

6. 实现查询: Utility.cs

using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.IO;
using Microsoft.Data.SqlXml;
using System.Threading;

/// <summary>
/// Summary description for Utility
/// </summary>

public static class Utility
{
    
const string CONNECTION_STRING = "Provider=SQLOLEDB;Data Source=JIANGJINNAN;User id=testUser;password=password;Initial Catalog=MyTestDb";

    
public static string SqlTemplaeFile
    

        
get
        
{
                
return HttpContext.Current.Server.MapPath("Template/Order_Sql.xml");
        }

    }


    
public static string XpathTemplaeFile
    
{
        
get
        
{
            
return HttpContext.Current.Server.MapPath("Template/Order_Xpath.xml");
        }

    }


    
public static string OrderXsltFile
    
{
        
get
        
{
            
return HttpContext.Current.Server.MapPath("Transform/Order.xsl");
        }

    }


    
public static string OrderDetailXsltFile
    
{
        
get
        
{
            
return HttpContext.Current.Server.MapPath("Transform/OrderDetail.xsl");
        }

    }


public static void ExecuteIntoStream(Stream stream, SqlXmlCommandType commandType, string commandText,object orderID,string xsltPath)
    
{
        
try
        
{
            SqlXmlCommand command 
= new SqlXmlCommand(CONNECTION_STRING);
            command.CommandType 
= commandType;
            command.CommandText 
= commandText;
            command.XslPath 
= xsltPath;
            command.ClearParameters();
            SqlXmlParameter parameter 
= command.CreateParameter();
            parameter.Name 
= "@orderID";            
            parameter.Value 
= orderID.ToString();
            command.ExecuteToStream(stream);
        }

        
catch (Exception ex)
        
{
            
throw ex;
        }
    
    }

}

这是真个查询的实现过程,我们通过方法ExecuteIntoStream把生成的XML,通过指定的XSLT转化生成HTML,最终他们一个Stream中——因为我们会在Web Page的Code Behind中直接调用这个方法,把HTML直接送到HttpResponseStream中,从而把它显示出来。在这个方法中我们可以看到,我们首先通过一个基于SQLOLEDB的Connection String 创建一个SqlXmlCommand,SqlXmlCommand和ADO.NET中的DbCommand很相似,用于执行所有的Data Access操作,我们想使用DbCommand一样为它指定CommandType,CommandText,Parameter,我们还可以通过XslPath属性把XSLT的路径传给SqlXmlCommand,他便会自动实现转化,最后调用ExecuteToStream把最终的结果方法Stream对象中。

7. 创建Web Page

现在我们来完成最后一步,创建两个Web Page。由于所有的Html都是通过SQLXML来实现的,所以连个Page不需要任何的HTML。只需要在Page Load 事件中编写下面的代码,就OK了。

Order.aspx.cs

protected void Page_Load(object sender, EventArgs e)
    
{
        
if (this.IsPostBack)
        
{
            
return;
        }


        
//Utility.ExecuteIntoStream(this.Response.OutputStream, SqlXmlCommandType.TemplateFile, Utility.SqlTemplaeFile, 0, Utility.OrderXsltFile);
        Utility.ExecuteIntoStream(this.Response.OutputStream, SqlXmlCommandType.TemplateFile, Utility.XpathTemplaeFile,"0", Utility.OrderXsltFile);
}

Utility.ExecuteIntoStream(this.Response.OutputStream, SqlXmlCommandType.TemplateFile, Utility.SqlTemplaeFile, 0, Utility.OrderXsltFile)和Utility.ExecuteIntoStream(this.Response.OutputStream, SqlXmlCommandType.TemplateFile, Utility.XpathTemplaeFile,"0", Utility.OrderXsltFile)具有一样的功能,大家可以使用试试,使用任何一个,得到的结果都是一样的。对于下面也一样.

using System;
using System.Data;
using System.Configuration;
using System.Collections;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using Microsoft.Data.SqlXml;

public partial class OrderDetail : System.Web.UI.Page
{
    
protected void Page_Load(object sender, EventArgs e)
    
{
        
if (this.IsPostBack)
        
{
            
return;
        }


        
int orderID = 1;
        
if (this.Request.QueryString["id"!= null)
        
{
            
if (!int.TryParse(this.Request.QueryString["id"], out orderID))
            
{
                orderID 
= 1;
            }

        }


        Utility.ExecuteIntoStream(
this.Response.OutputStream, SqlXmlCommandType.TemplateFile, Utility.SqlTemplaeFile, orderID, Utility.OrderDetailXsltFile);
        
//Utility.ExecuteIntoStream(this.Response.OutputStream, SqlXmlCommandType.TemplateFile, Utility.XpathTemplaeFile, orderID.ToString(), Utility.OrderDetailXsltFile);

    }

}
posted @ 2007-03-26 02:59  Artech  阅读(11848)  评论(37编辑  收藏  举报