中立合約 (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);
}
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 (命名空間雜湊),確保唯一性
[DataContract(Name = "BillOfOrderHeaderOrderDetailLZ9Dq20o", Namespace = "...")]
public partial class BillOfOrderHeaderOrderDetailLZ9Dq20o : IExtensibleDataObject, INotifyPropertyChanged
{
[DataMember] public OrderDetail[] DetailList { get; set; }
[DataMember] public OrderHeader Header { get; set; }
// ... ExtensionData, PropertyChanged 實作
}
[DataContract(Name = "OrderBill")] // 錯誤!若同服務有 SalesBill 會衝突
public class Bill<THeader, TDetail> { ... }
同一服務若同時使用 Bill<OrderHeader, OrderDetail> 與 Bill<SalesHeader, SalesDetail>,固定名稱會導致「伺服器無法處理要求」錯誤。
[DataContract(Name = "Bill_{0}_{1}")]
public class Bill<THeader, TDetail> { ... }
產生:Bill_OrderHeader_OrderDetail、Bill_SalesHeader_SalesDetail。
若存在 NamespaceA.OrderHeader 與 NamespaceB.OrderHeader,上述方案仍會產生相同名稱。
[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 屬性區分作業名稱 (如 ProcessOrderBill、ProcessSalesBill)。IList<T>) 序列化為陣列,用戶端可透過「設定服務參考」改為 List<T>。Bill<Header, List<Detail>>),會導致極長且難以維護的型別名稱。WCF 透過具體化機制支援泛型資料合約,關鍵在於命名策略。採用 Name="Type_{0}_{1}_{#}" 模板可有效避免命名衝突,確保服務演進穩定。下一篇將回顧資料合約核心概念與對應機制。