首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> 關於.NET

談 WCF Data Contract (二):泛型型別的序列化支援與命名策略

     編輯:關於.NET

前言

中立合約 (Neutral Contract) 不支援泛型,WCF 必須將泛型型別「具體化」為特定的封閉型別。本文探討泛型資料合約的序列化呈現、預設命名規則、命名衝突問題與解決策略。

範例:泛型單據類別


[DataContract]
public class Bill<THeader, TDetail>
{
    [DataMember] public THeader Header { get; set; }
    [DataMember] public IList<TDetail> DetailList { get; set; }
}

[DataContract] public class OrderHeader { [DataMember] public Guid OrderID; [DataMember] public DateTime OrderDate; }
[DataContract] public class OrderDetail { [DataMember] public Guid ProductID; [DataMember] public int Quantity; }

服務合約具體化泛型


[ServiceContract]
public interface IBillManager
{
    [OperationContract]
    void Process(Bill<OrderHeader, OrderDetail> orderBill);
}

XSD 序列化結果

WCF 產生具體型別 BillOfOrderHeaderOrderDetailLZ9Dq20o:


<xs:complexType name="BillOfOrderHeaderOrderDetailLZ9Dq20o">
  <xs:annotation>
    <xs:appinfo>
      <GenericType Name="BillOf{0}{1}{#}" Namespace="...">
        <GenericParameter Name="OrderHeader" Namespace="..."/>
        <GenericParameter Name="OrderDetail" Namespace="..."/>
      </GenericType>
    </xs:appinfo>
  </xs:annotation>
  <xs:sequence>
    <xs:element name="DetailList" nillable="true" type="tns:ArrayOfOrderDetail"/>
    <xs:element name="Header" nillable="true" type="tns:OrderHeader"/>
  </xs:sequence>
</xs:complexType>

觀察重點:

  • 泛型類別消失,取而代之的是具體化型別名稱
  • DetailList 從 IList<TDetail> 變為 OrderDetail[] (陣列)
  • 型別名稱包含雜湊值 LZ9Dq20o (命名空間雜湊),確保唯一性

Proxy 產生的類別結構


[DataContract(Name = "BillOfOrderHeaderOrderDetailLZ9Dq20o", Namespace = "...")]
public partial class BillOfOrderHeaderOrderDetailLZ9Dq20o : IExtensibleDataObject, INotifyPropertyChanged
{
    [DataMember] public OrderDetail[] DetailList { get; set; }
    [DataMember] public OrderHeader Header { get; set; }
    // ... ExtensionData, PropertyChanged 實作
}

命名衝突問題與解決方案

問題 1:固定 Name 導致衝突


[DataContract(Name = "OrderBill")] // 錯誤!若同服務有 SalesBill 會衝突
public class Bill<THeader, TDetail> { ... }

同一服務若同時使用 Bill<OrderHeader, OrderDetail> 與 Bill<SalesHeader, SalesDetail>,固定名稱會導致「伺服器無法處理要求」錯誤。

方案 1:使用佔位符 {0}、{1} 代表泛型參數名稱


[DataContract(Name = "Bill_{0}_{1}")]
public class Bill<THeader, TDetail> { ... }

產生:Bill_OrderHeader_OrderDetail、Bill_SalesHeader_SalesDetail。

問題 2:不同命名空間同名型別仍衝突

若存在 NamespaceA.OrderHeader 與 NamespaceB.OrderHeader,上述方案仍會產生相同名稱。

方案 2:加入雜湊值佔位符 {#}


[DataContract(Name = "Bill_{0}_{1}_{#}")]
public class Bill<THeader, TDetail> { ... }

產生:Bill_OrderHeader_OrderDetail_LZ9Dq20o、Bill_SalesHeader_SalesDetail_XYz123。{#} 為泛型參數命名空間的雜湊值,確保全域唯一。

命名規則總結表

DataContract Name 模板產生範例適用場景
未指定 (預設)BillOfOrderHeaderOrderDetailLZ9Dq20o單一泛型具體化,無衝突風險
"OrderBill"OrderBill僅一個具體化,**極不建議**
"Bill_{0}_{1}"Bill_OrderHeader_OrderDetail同命名空間、不同泛型參數
"Bill_{0}_{1}_{#}"Bill_OrderHeader_OrderDetail_LZ9Dq20o**最佳實務**:跨命名空間、多組具體化

最佳實務建議

  • 泛型資料合約**務必指定 Name 模板**,包含 {#} 確保唯一性。
  • 服務合約中每個泛型具體化對應一個作業,使用 Name 屬性區分作業名稱 (如 ProcessOrderBill、ProcessSalesBill)。
  • 集合屬性 (IList<T>) 序列化為陣列,用戶端可透過「設定服務參考」改為 List<T>。
  • 避免巢狀泛型 (如 Bill<Header, List<Detail>>),會導致極長且難以維護的型別名稱。

結語

WCF 透過具體化機制支援泛型資料合約,關鍵在於命名策略。採用 Name="Type_{0}_{1}_{#}" 模板可有效避免命名衝突,確保服務演進穩定。下一篇將回顧資料合約核心概念與對應機制。

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