UML软件工程组织

浅析.NET下XML数据访问新机制
作者:王凯明 选自赛迪网
 

一.前言:

  XML作为Web服务的基石,其重要性自然是不言而喻的,它正日益被开发人员所重视。同时随着各类新兴开发工具的推出,XML数据的访问机制也变得越来越灵活多样。.Net框架为开发人员提供了几种新的XML数据访问机制,每种机制都提供了不同的XML数据访问支持。所以对于开发人员而言,选择正确合适的XML数据访问机制变得相当重要,它会影响到项目开发的复杂度以及应用程序的整体效率。

  大体来讲,.Net框架为开发人员提供了ADO.Net中的数据集XML数据访问机制和SqlDataReader XML数据访问机制这两种新的机制。为方便起见,我们不妨将前一种机制称为ADO.Net机制,而后一种为SqlDataReader机制。本文我就主要向大家介绍这两种机制,同时我还会介绍SQLXML和ADO 2.6这两种比较传统的XML数据访问机制,并在最后将它们的性能做一个比较,以使读者更清楚在具体的应用中应选择哪种机制。本文的实例程序将用到SQL Server中的Northwind数据库,通过访问该数据库我们可以获取相应的XML数据并据此对不同的访问机制进行性能比较和分析。

二.各种机制介绍:

  ADO.Net是ADO升级版本,它为我们提供了全新的数据访问机制,在提高数据访问效率的同时它也大大简化了开发人员的工作量,所以熟练掌握和运用ADO.Net进行开发是.Net开发人员不可或缺的基本能力。

  ADO.Net为开发人员提供了两种数据访问模式:一种为连接模式(Connected),另一种为非连接模式(Disconnected)。前一种模式和传统的ADO模式是一样的,而后一种模式则为.Net所独有也是为.Net所推荐的,该模式的核心部分为ADO.Net中的数据集对象(DataSet)。数据集对象的类位于System.Data命名空间中,它对XML有相当完善的支持。在非连接模式下工作时,数据集对象能以XML的形式将数据存储在主存中并提供用户操作使用,因此通过一个简单的方法它就能将关系型的数据转化为层次化的具有良好模式的XML数据,这就是前面提到的ADO.Net机制。

  下面,我就通过建立一个实例程序向大家介绍该机制下的XML数据访问的具体方法。首先新建一个控制台应用程序的C#项目,该实例程序主要显示了不同的XML数据访问机制的运行效率。同时添加一个新的类,文件名不妨命名为DBXml.cs,该类包含了几种XML数据访问机制的不同实现方法,它们分别是:ExecuteADONetSelect、ExecuteSqlDataReaderSelect以及ExecuteSqlXmlSelect,分别代表了ADO.Net机制、SqlDataReader机制和SQLXML机制,而ADO 2.6这种机制则会在后面介绍。接着,为该类添加必要的命名空间。


using System;
using System.Data;
using System.Data.SqlClient;
using System.Text;
using System.Xml;
using Microsoft.Data.SqlXml;


其中对于Microsoft.Data.SqlXml命名空间需要先添加对Microsoft.Data.SqlXml组件的引用,而该组件是在安装了SQLXML 3.0以后就会有的,添加的方法如图1所示:



图1


现在为该类添加ExecuteADONetSelect()方法,该方法执行了对数据库的SELECT查询操作,具体实现如下:


public string ExecuteADONetSelect(string CustomerID, 
  string ConnectionString)
  {
    // 创建一个数据库连接对象
    SqlConnection myConnection = new SqlConnection(ConnectionString);
    // 创建一个数据适配器对象
    SqlDataAdapter mySqlDataAdapter1 = new SqlDataAdapter("SELECT *
    FROM customers WHERE CustomerID = @CustomerID", myConnection);
    // 为其参数集添加参数 
    mySqlDataAdapter1.SelectCommand.Parameters.Add("@CustomerID",
      SqlDbType.Char, 5, "CustomerID");
    mySqlDataAdapter1.SelectCommand.Parameters["@CustomerID"].
      Value = CustomerID;
    // 创建一个数据集对象
    DataSet myDataSet = new DataSet();
    // 运用数据集对象的Fill方法来填充数据集对象
    mySqlDataAdapter1.Fill(myDataSet,"Customers");
    // 关闭数据库连接
    myConnection.Close();
    // 返回数据集对象中的数据的XML形式
    return myDataSet.GetXml();
}


以上ExecuteADONetSelect()方法首先根据传入的数据库连接字符串创建一个SqlConnection对象,然后创建一个SqlDataAdapter对象,并传递一条SELECT语句和上面的SqlConnection对象到其构造函数中。其中的SELECT语句中包含了一个用户ID参数,所以你必须将该参数添加到SqlDataAdapter对象的SelectCommand参数集中并为该参数赋值。接下来的两步创建了一个新的DataSet对象并用上面的SqlDataAdapter对象填充了该DataSet对象,这两步虽然简单,但它们却完成了大部分的工作。最后,关闭数据库连接并以XML的形式返回数据集对象中的数据。

接下来为该类添加ExecuteSqlDataReaderSelect()方法,该方法如下:


public string ExecuteSqlDataReaderSelect(string CustomerID, 
  string ConnectionString)
 { 
    SqlDataReader myDataReader = null;
    // 创建一个数据库连接对象
    SqlConnection mySqlConnection = new SqlConnection
     (ConnectionString);
    // 创建一个数据库命令对象,并在SQL语句中运用FOR XML子句
    SqlCommand mySqlCommand = new SqlCommand("SELECT * FROM Customers 
      WHERE CustomerID = @CustomerID FOR XML RAW", mySqlConnection);
    mySqlCommand.CommandType = CommandType.Text;
    // 为其参数集添加参数
    mySqlCommand.Parameters.Add("@CustomerID", SqlDbType.Char, 5,
    "CustomerID");
    mySqlCommand.Parameters["@CustomerID"].Value = CustomerID;
     mySqlConnection.Open();
    // 执行数据库命令对象的ExecuteReader操作以取得一个数据阅读器对象
    myDataReader = mySqlCommand.ExecuteReader(CommandBehavior.
     CloseConnection);
   // 创建一个StringBuilder对象以构造XML字符串
   StringBuilder xml = new StringBuilder(8192);
   // 不断从数据阅读器中获取XML数据并添加到StringBuilder对象中
     while (myDataReader.Read())
     {
      if (!myDataReader.IsDBNull(0))
      xml.Append(myDataReader.GetString(0));
     }
    myDataReader.Close();
    mySqlConnection.Close();
    // 返回XML数据的字符串形式
    return xml.ToString();
 }


SqlDataReader类为我们提供了一种只读的、向前的数据访问方式,该特性使得它成为访问SQL Server数据库速度最快的一种方法。然而SqlDataReader类并不直接支持XML类型的数据,你必须手动编写代码将数据转换成XML数据或者通过运行FOR XML查询子句来返回XML类型的数据。这里,我们将使用FOR XML RAW子句来获取XML数据。

上面的ExecuteSqlDataReaderSelect()方法首先声明一个SqlDataReader对象,然后通过函数的数据库连接字符串参数创建一个SqlConnection对象,并根据带有FOR XML RAW子句的SELECT语句和上面的SqlConnection对象创建一个SqlCommand对象。之后,该方法为SqlCommand的参数集添加参数并为用户ID参数赋值。打开SqlConnection连接后,该方法将SqlCommand对象的ExecuteReader()方法的返回值赋给SqlDataReader对象,并通过一个StringBuilder对象读取SqlDataReader对象中的XML数据。最后,该方法关闭SqlDataReader对象和SqlConnection对象并以字符串的形式返回XML数据。

最后,为该类添加ExecuteSqlXmlSelect()方法,该方法如下:


public string ExecuteSqlXmlSelect(string CustomerID, string
  ConnectionString, bool ClientSide)
{
// 创建一个SqlXmlCommand对象
   SqlXmlCommand cmd = new SqlXmlCommand(ConnectionString);
   cmd.RootTag = "Customers";
   cmd.ClientSideXml = ClientSide;
   cmd.CommandText = "SELECT * FROM Customers WHERE CustomerID =
    '" + CustomerID + "' FOR XML RAW";
 // 执行SqlXmlCommand对象的ExecuteXmlReader操作以取得一个XmlReader对象
   XmlReader xr = cmd.ExecuteXmlReader();
 // 创建一个XmlDocument对象
   XmlDocument xd = new XmlDocument();
 // 运用其Load方法将获取的XML数据载入到一个DOM中并返回XML数据的字符串形式
   xd.Load(xr);
   return xd.OuterXml
 }


  SQLXML托管类是SQLXML 3.0包的一部分,SQLXML 3.0包扩展了SQL Server 2000的XML功能。SQLXML托管类是原生的.NET类,它提供了通过编程方法访问XML数据的功能。上面的ExecuteSqlXmlSelect()方法首先运用数据库连接字符串创建一个SqlXmlCommand对象。同时,由FOR XML子句获取的XML数据往往只是一个XML片段,并不是结构良好(Well-Formed)的XML数据,要使得XML成为结构良好的,你必须设定SqlXmlCommand对象的RootTag属性。在本实例中,我们将该属性设置为"Customers"。

  以前版本的SQLXML包是在服务器端生成XML数据,然后传递给客户端的。这样,因为服务器端返回的XML数据流远远大于原来的二进制格式数据流,所以就产生了一个可扩展性问题。而3.0版本的SQLXML包允许服务器将数据以二进制的格式传递给客户端,然后在客户端将数据进一步转变为XML格式,这样就解决了原来版本中存在的可扩展性问题了。在3.0版本的SQLXML包中实现这个特性的方法很简单,只要将SqlXmlCommand对象的ClientSideXml属性设置为True即可。这样,程序会继续使用带有FOR XML子句的SELECT语句,但是托管类会在发送SQL语句到SQL Server之前将FOR XML子句先剥离掉。数据库就不会发现FOR XML子句,因此从服务器返回给客户端的数据也就是二进制格式的了。客户端获取从服务器返回的数据后,托管类会进而将数据转化为XML格式。

  上面的程序中,我们通过执行SqlXmlCommand对象的ExecuteXmlReader()方法来获取一个XmlReader对象,并用它来填充一个XmlDocument对象,最后返回XmlDocument对象的OuterXml属性给调用者。

  上面,我给大家介绍了ADO.Net机制、SqlDataReader机制和SQLXML机制这三种不同的方法,下面是运用这三种不同方法所得到的一个结果示意图,从图中的结果读者可以看出那种方法的效率最高。



图2


  最后要介绍的是ADO 2.6机制,该机制是在VB 6.0中实现的。我们先创建一个名为DBXMLVS6的ActiveX DLL项目,然后把Class1修改为ADO26,接着添加一个到ActiveX Data Objects 2.6的引用。一旦完毕,为该类添加一个ExecuteSelect()方法如下:


Public Function ExecuteSelect(
	CustomerID As String,
	ConnectionString As String) 
	As String
	Set conn = New ADODB.Connection
	conn.ConnectionString = ConnectionString
	conn.Open

	Set cmd = New ADODB.Command
	cmd.ActiveConnection = conn
	cmd.CommandText = "select * from customers 
	 where CustomerID = '" & CustomerID & "'" 
	Set rs = cmd.Execute

	Set adoStream = New ADODB.Stream
	adoStream.Type = adTypeText
	adoStream.Open    
	rs.Save adoStream, adPersistXML

	 '返回XML字符串
	 ExecuteSelect = adoStream.ReadText()
End Function


  和其他实例一样,上面的程序创建并打开一个数据库连接。然后创建一个Command对象,并将它的ActiveConnection属性设置为上面的Connection对象,而程序中用到的SELECT语句和前面实例中的是一样的。接着,程序将Command对象的Execute()方法的返回值传递给一个RecordSet对象。然后,程序创建一个ADO Stream对象,并将其Type属性设置为adTypeText同时打开它。一旦该流对象被打开,调用RecordSet对象的Save()方法就能将数据以XML的格式保存。最后,程序通过Stream对象的ReadText()方法返回XML数据给调用者。

  结合以上介绍的四种不同的XML数据访问机制,我用ASP和ASP.Net页面以调用组件的方式对其XML数据访问的性能和可扩展性进行了测试,结果得出如下图所示的性能分析结果,以供读者参考。



图3


三.总结:

  本文我向大家介绍了.Net框架下XML数据访问的四种不同机制,随着微软不断的添加新功能并提高性能,XML数据访问会继续不停的向前发展。同时,鉴于新版本的SQL Server数据库服务器会添加不少对XML的原生支持,我们可以期盼不久的将来XML数据访问将得到极大的发展。

 

版权所有:UML软件工程组织