WCF 預設將 List<T>、陣列等標準集合序列化為陣列格式。但當業務需求涉及:
此時必須實作自訂集合型別並正確套用資料合約屬性。
[DataContract]
public class Document
{
[DataMember]
public string LocalPath { get; set; }
[DataMember]
public string FileName { get; set; }
[DataMember]
public FileType FileType { get; set; }
}
public enum FileType { TXT, DOC, HTML, OTHER }
// 關鍵:使用 CollectionDataContract 而非 DataContract
[KnownType(typeof(Document))]
[CollectionDataContract(Name = "DocumentList", ItemName = "Document")]
[Serializable]
public class DocumentList : IList<Document>
{
private readonly List<Document> _innerList = new List<Document>();
// 完整實作 IList<Document> 介面方法...
public int IndexOf(Document item) => _innerList.IndexOf(item);
public void Insert(int index, Document item) => _innerList.Insert(index, item);
public void RemoveAt(int index) => _innerList.RemoveAt(index);
public Document this[int index] { get => _innerList[index]; set => _innerList[index] = value; }
public void Add(Document item) => _innerList.Add(item);
public void Clear() => _innerList.Clear();
public bool Contains(Document item) => _innerList.Contains(item);
public void CopyTo(Document[] array, int arrayIndex) => _innerList.CopyTo(array, arrayIndex);
public int Count => _innerList.Count;
public bool IsReadOnly => _innerList.IsReadOnly;
public bool Remove(Document item) => _innerList.Remove(item);
public IEnumerator<Document> GetEnumerator() => _innerList.GetEnumerator();
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => _innerList.GetEnumerator();
}
| 屬性 | 作用 | 必要性 |
|---|---|---|
[CollectionDataContract] |
標記為集合資料合約,控制序列化為 XML 元素序列而非陣列 | 必要 |
[KnownType(typeof(Document))] |
告知序列化器集合項目型別,確保反序列化正確 | 必要 |
[Serializable] |
支援二進位序列化(如透過 NetTcpBinding) | 建議 |
Name/ItemName |
自訂 XML 元素名稱(預設為型別名) | 可選 |
[ServiceContract]
public interface IDocumentService
{
[OperationContract]
[FaultContract(typeof(DirectoryNotFoundException))]
DocumentList FetchDocuments(string homeDir);
}
[DataContract] // 錯誤!會導致序列化為物件而非集合
public class DocumentList : IList<Document> { ... }
結果:用戶端收到的集合屬性為 null 或結構錯誤。
序列化器無法識別集合項目型別,導致反序列化失敗或項目型別變為 object。
[CollectionDataContract(Name = "DocumentCollection", ItemName = "Doc")]
public class DocumentCollection : List<Document> { }
產生的 XML:<DocumentCollection><Doc>...</Doc></DocumentCollection>
[CollectionDataContract]
[KnownType(typeof(Document))]
public class PagedDocumentList : List<Document>
{
[DataMember]
public int TotalCount { get; set; }
[DataMember]
public int PageIndex { get; set; }
[DataMember]
public int PageSize { get; set; }
}
序列化結果同時包含項目清單與分頁中繼資料,極大簡化用戶端邏輯。
WCF 自訂集合的核心在於:實作 IList<T> + 套用 CollectionDataContract + 標註 KnownType。避開 DataContract 陷阱,善用 Name/ItemName 控制 XML 結構,並可擴充業務屬性,讓集合不只是資料容器,更成為豐富的資料傳輸物件(DTO)。