服務合約設計直接決定系統的耦合度、可重用性與演進彈性。WCF 合約本質為 .NET 介面加上屬性,但 ServiceContractAttribute 不支援繼承 (Inherited = false),導致合約層級設計有特殊限制。
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface, Inherited = false)]
public sealed class ServiceContractAttribute : Attribute { }
規則:每一層介面皆須標註 [ServiceContract],否則該層作業合約不會被公開。
[ServiceContract(Namespace = "https://ns")]
public interface IVehicle { [OperationContract] string Run(); }
[ServiceContract(Namespace = "https://ns")]
public interface ITruck : IVehicle { [OperationContract] string Carry(); }
[ServiceContract(Namespace = "https://ns")]
public interface ICar : IVehicle { [OperationContract] string Take(); }
宿主端點繫結至最底層介面 (ITruck),單一端點暴露完整作業集 (Run + Carry)。
匯入中繼資料時,繼承階層會被扁平化:產生單一介面 ITruck 包含所有作業 (Run, Carry),失去原始階層。
對策:手動調整 Proxy 程式碼恢復階層,或接受扁平化 (單一介面) 簡化呼叫。
遵循 SOLID 原則:
反模式:單一 IVehicle 包含 Run、Take、Carry → 強迫貨車實作 Take、轎車實作 Carry。
正確分解:
[ServiceContract] interface IVehicle { [OperationContract] string Run(); }
[ServiceContract] interface ITruck : IVehicle { [OperationContract] string Carry(); }
[ServiceContract] interface ICar : IVehicle { [OperationContract] string Take(); }
ITruck (Run + Carry)。ICar (Run + Take)。勿設計:[OperationContract] string GetCarNumber(); (類似 Property Getter)。
服務應封裝狀態管理,客戶端呼叫行為導向作業 (如 RegisterVehicle),而非存取屬性。
WCF 合約設計需兼顧 伺服端繼承限制 (每層加 ServiceContract)、客戶端扁平化現實 與 SOA 設計原則 (SRP/ISP/粒度平衡)。合理分解合約、控制作業數量、避免屬性式作業,是打造可維護、可演進服務介面的關鍵。