首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 數據庫知識 >  >> SQL Server 資料庫 >> 關於SqlServer

SQL Server 2005 XML 資料型別客戶端開發指南:ADO.NET 2.0 整合實戰(上)

     編輯:關於SqlServer

一、XML 型別成為一等公民:TDS 協議層面的變革

SQL Server 2005 引入原生 `xml` 資料型別,TDS (Tabular Data Stream) 協議新增專屬型別標識符 `0xF1` (XML_F52E2B61-18A1-11d1-B105-000805F49916B)。查詢分析器顯示為單欄單列實為 XML 串流,客戶端必須使用 SqlCommand.ExecuteXmlReader 正確讀取。

二、FOR XML 增強:從串流到型別化 XML

功能SQL 2000SQL 2005 新增
模式RAW, AUTO, EXPLICITPATH (簡化屬性/元素控制,替代 EXPLICIT)
回傳型別僅 XML 串流 (特殊 TDS 型別)TYPE 指令 直接回傳 `xml` 型別
巢狀查詢不支援巢狀 FOR XML 子查詢產生階層結構
根元素ROOT 指令僅支援片段ROOT 可產生完整 XML 文件
結構描述無XMLSCHEMA 指令內嵌 XSD

範例:PATH 模式 + TYPE 回傳 xml 型別

SELECT
    OrderID AS '@OrderID',
    OrderDate AS '@Date',
    (SELECT ProductID, Qty, Price
     FROM OrderDetails od
     WHERE od.OrderID = o.OrderID
     FOR XML PATH('Item'), TYPE) AS Items
FROM Orders o
WHERE CustomerID = 'ALFKI'
FOR XML PATH('Order'), ROOT('Orders'), TYPE;

三、ADO.NET 2.0 SqlClient 增強

1. 新型別枚舉值

// System.Data.DbType
DbType.Xml

// System.Data.SqlDbType
SqlDbType.Xml

2. SqlXml 類別:XML 值的標準載體

namespace System.Data.SqlTypes
{
    public sealed class SqlXml : INullable
    {
        public static SqlXml Null { get; }
        public bool IsNull { get; }
        public XmlReader CreateReader(); // 核心方法:取得唯讀、前進式 XmlReader
        public void WriteTo(XmlWriter writer);
        // 隱式轉換:string → SqlXml, XmlReader → SqlXml
    }
}

3. DataSet 支援 Xml 型別資料行

DataTable dt = new DataTable();
dt.Columns.Add("DocId", typeof(int));
dt.Columns.Add("Content", typeof(SqlXml)); // 或 typeof(string) 配合 DbType.Xml

// Fill 時自動將 xml 型別欄位映射為 SqlXml 實例

四、客戶端完整存取模式

模式 A:ExecuteXmlReader — 針對 FOR XML ... TYPE 查詢

using (SqlConnection conn = new SqlConnection(connStr))
using (SqlCommand cmd = new SqlCommand(
    "SELECT Content FROM Docs WHERE DocId = @id FOR XML PATH('Doc'), TYPE", conn))
{
    cmd.Parameters.Add("@id", SqlDbType.Int).Value = 1;
    conn.Open();
    using (XmlReader reader = cmd.ExecuteXmlReader())
    {
        // 處理單一 XML 文件 (可能包含多個根元素)
        while (reader.Read())
        {
            // 邏輯處理...
        }
    }
}

模式 B:SqlParameter + SqlDbType.Xml — 參數化傳入/輸出 XML

// 輸入 XML 參數
string xmlInput = @"";
using (SqlCommand cmd = new SqlCommand("usp_ProcessOrder", conn))
{
    cmd.CommandType = CommandType.StoredProcedure;
    cmd.Parameters.Add("@OrderXml", SqlDbType.Xml).Value = new SqlXml(new XmlTextReader(new StringReader(xmlInput)));
    // 或直接 .Value = xmlInput (隱式轉換)
    cmd.ExecuteNonQuery();
}

// 輸出 XML 參數
SqlParameter outParam = cmd.Parameters.Add("@ResultXml", SqlDbType.Xml);
outParam.Direction = ParameterDirection.Output;
cmd.ExecuteNonQuery();
SqlXml resultXml = (SqlXml)outParam.Value;
using (XmlReader r = resultXml.CreateReader()) { /* ... */ }

模式 C:DataAdapter Fill — 將 xml 欄位載入 DataTable

DataTable dt = new DataTable();
using (SqlDataAdapter da = new SqlDataAdapter("SELECT DocId, Content FROM Docs", conn))
{
    da.Fill(dt);
    foreach (DataRow row in dt.Rows)
    {
        SqlXml xml = (SqlXml)row["Content"];
        if (!xml.IsNull)
        {
            XPathDocument doc = new XPathDocument(xml.CreateReader());
            // XPath/XSLT 處理...
        }
    }
}

五、伺服器端 XML 方法與客戶端協同

伺服器方法客戶端對應處理典型用途
query()`SqlXml` → `XmlReader` → `XDocument.Load` / `XPathNavigator`擷取 XML 片段
value()直接回傳 CLR 型別 (int, string, DateTime...)純量值投影,避免 XML 解析
exist()回傳 `bool` / `SqlBoolean`WHERE 篩選,下推至伺服器
nodes()多資料列結果集,正常 `SqlDataReader` 讀取XML 切碎為關係列,客戶端無感知
modify()僅 `UPDATE` 陳述式,客戶端 `ExecuteNonQuery`部分 XML 更新

六、效能最佳實踐

  1. 優先使用 `value()` / `exist()`:將篩選/投影下推伺服器,減少網路傳輸與客戶端解析
  2. 大型 XML 分塊讀取:`ExecuteXmlReader` 配合 `XmlReader` 流式處理,避免載入完整 DOM
  3. 參數化 XML 輸入:使用 `SqlDbType.Xml` 參數,防止注入、利用計劃快取
  4. 避免 `SELECT *` 含 XML 欄位:明確列出所需欄位,或用 `value()` 只取純量
  5. 連線池與非同步:`BeginExecuteXmlReader` / `EndExecuteXmlReader` 實現非同步管線

七、常見陷阱與解決方案

問題原因解決
XmlReader 讀取後無法重置前進式唯讀流需重複查詢或改用 `XDocument.Load(reader)` 緩存
FOR XML 無 TYPE 回傳亂碼TDS 特殊型別被視為字串必須加 `TYPE` 指令或用 `ExecuteXmlReader`
大量 XML 參數導致 LOH 壓力字串 > 85KB 進入 Large Object Heap使用 `SqlXml` + `XmlReader` 流式傳遞,避免大字串
DataSet 序列化 XML 欄位失敗Remoting/WCF 不支援 SqlXml轉為 `string` 或 `byte[]` 傳輸,接收端重建 `SqlXml`

八、完整範例:訂單系統 XML 工作流

// 1. 取得客戶訂單摘要 (伺服器端 FOR XML PATH + TYPE)
const string sql = @"
    SELECT
        CustomerID AS '@Id',
        (SELECT OrderID, OrderDate, TotalAmt
         FROM Sales.Orders
         WHERE CustomerID = c.CustomerID
         FOR XML PATH('Order'), TYPE) AS Orders
    FROM Sales.Customers c
    WHERE CustomerID = @CustId
    FOR XML PATH('Customer'), ROOT('Data'), TYPE;";

using (SqlConnection conn = new SqlConnection(connStr))
using (SqlCommand cmd = new SqlCommand(sql, conn))
{
    cmd.Parameters.Add("@CustId", SqlDbType.NVarChar, 5).Value = "ALFKI";
    conn.Open();
    using (XmlReader reader = cmd.ExecuteXmlReader())
    {
        XDocument doc = XDocument.Load(reader);
        // 2. 客戶端 LINQ to XML 轉換為 HTML 報表
        var html = new XElement("table",
            doc.Descendants("Order").Select(o =>
                new XElement("tr",
                    new XElement("td", (string)o.Attribute("OrderID")),
                    new XElement("td", (DateTime)o.Attribute("OrderDate")),
                    new XElement("td", (decimal)o.Attribute("TotalAmt"))
                )));
        File.WriteAllText("OrderReport.html", html.ToString());
    }
}

下篇將深入:`SqlXml` 在 Entity Framework Core / Dapper 中的型別映射、XML 結構描述集合驗證的客戶端/伺服器端協同、大型 XML 串流的非同步管線設計、以及雲端遷移 (Azure SQL / SQL MI) 的 XML 型別相容性測試。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved