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

WCF 進階:拒絕代理生成工具,實現客戶端與服務端代碼共享

     編輯:關於.NET

前言

傳統 WCF 開發依賴 svcutil.exe 或 Visual Studio「加入服務參考」產生 Proxy 類別,導致服務端精心設計的領域模型 (不可變物件、驗證邏輯、計算屬性、特定集合類型) 在客戶端退化為單純的 DTO (Data Transfer Object)。本文展示如何繞過代理生成,直接共享 服務合約介面與資料契約類別,並構建無 Proxy 的 ClientBase<T> 衍生類別,同時支援單元測試依賴注入。

為何放棄代理生成工具?

1. 不可變物件與 DataContract 序列化

領域模型常將屬性設為 private set 確保不可變性,但代理生成工具會產生可讀寫屬性並實作 INotifyPropertyChanged,破壞領域模型完整性。共享原始類別可保留 private set 與建構函式驗證邏輯。

[DataContract(Namespace = "https://zsr.codeplex.com/services/")]
public class ZombieTypeSummary {
    public ZombieTypeSummary(string zombieTypeName, int zombieTypeKey, string briefDescription = null, Uri thumbnailImage = null) {
        ZombieTypeName = zombieTypeName; ZombieTypeKey = zombieTypeKey; BriefDescription = briefDescription; ThumbnailImage = thumbnailImage;
    }
    [Obsolete("This is only used by the DataContractSerializer", true)]
    public ZombieTypeSummary() { } // 僅供序列化器使用
    [DataMember] public string ZombieTypeName { get; private set; }
    [DataMember] public int ZombieTypeKey { get; private set; }
    [DataMember] public string BriefDescription { get; private set; }
    [DataMember] public Uri ThumbnailImage { get; private set; }
}

2. 驗證邏輯、計算屬性與部分類別限制

  • 代理類別為生成代碼,難以注入驗證屬性 (如 [Required]) 或計算屬性。
  • 雖可用 partial 類別分離手寫邏輯,但無法攔截屬性 setter (缺少 XxxChanging/Changed 部分方法)。
  • 共享原始類別則完整保留所有業務邏輯。

3. 集合類型資訊遺失

代理工具將所有集合統一為 IList<T>,喪失 ObservableCollection<T>、BindingList<T> 等具體類型,影響 UI 綁定與變更通知。

4. 建置流程整合困難

代理生成需服務端運行,難以納入自動化建置,導致開發者手動更新、版本不一致。

實作無 Proxy WCF 客戶端

核心模式:繼承 ClientBase<TChannel>

using System.ServiceModel;
using System.ServiceModel.Channels;

namespace Zombie.Services.Definitions {
    public class InformationClient : ClientBase<IInformationService> {
        public new IInformationService Channel { get { return base.Channel; } }
        public InformationClient() { }
        public InformationClient(string endpointConfigurationName) : base(endpointConfigurationName) { }
        public InformationClient(string endpointConfigurationName, string remoteAddress) : base(endpointConfigurationName, remoteAddress) { }
        public InformationClient(string endpointConfigurationName, EndpointAddress remoteAddress) : base(endpointConfigurationName, remoteAddress) { }
        public InformationClient(Binding binding, EndpointAddress remoteAddress) : base(binding, remoteAddress) { }
    }
}

支援依賴注入 (單元測試)

private IInformationService _mockService;
public InformationClient(IInformationService mockService) : base(new BasicHttpBinding(), new EndpointAddress("https://fakeAddress.com")) {
    _mockService = mockService;
}
public new IInformationService Channel { get { return _mockService ?? base.Channel; } }
protected override IInformationService CreateChannel() { return _mockService ?? base.CreateChannel(); }
public new void Open() { if (_mockService == null) base.Open(); }

測試時傳入 Mock 物件,生產環境使用真實端點。注意:此模式非完全封裝,已知限制為可轉型為基類呼叫真實 Open(),但配合假地址可防誤連線。

部分代碼共享策略

當服務端與客戶端 (如 .NET 與 WinRT) 僅需共享類別部分邏輯:

  1. 關聯檔案 + 條件編譯:單一檔案,#if CLIENT 區隔平台差異。優點:同步容易;缺點:雜亂。
  2. 關聯檔案 + partial 類別分檔:共享核心邏輯檔 (如 ZombieTypeSummary.shared.cs),平台特有邏輯放入各自專案的 ZombieTypeSummary.client.cs / .server.cs。優點:結構清晰;缺點:需維護多檔案。

Silverlight 相容性考量

Silverlight WCF 僅支援 APM 模式 (Begin/End),需將 Task 介面包裝:

[ServiceContract(Namespace = "...")]
public interface IInformationService {
    [OperationContract(AsyncPattern = true)] IAsyncResult BeginListZombieTypes(AsyncCallback cb, object state);
    ZombieTypeSummaryCollection EndListZombieTypes(IAsyncResult result);
    // ...
}
public static class InformationServiceExtensions {
    public static Task<ZombieTypeSummaryCollection> ListZombieTypes(this IInformationService client) {
        return Task.Factory.FromAsync(client.BeginListZombieTypes, client.EndListZombieTypes, null);
    }
    // ...
}

總結

共享服務合約與資料契約組件,配合 ClientBase<T> 衍生類別,能消除 Proxy 產生的諸多弊端:保留領域模型豐富性、集合類型精確性、建置自動化,並自然支援單元測試。此模式適用於 .NET 全平台 (含 Silverlight/WinRT),是現代 WCF 開發的推薦實踐。專案完整碼託管於 CodePlex - Zombie Standard Reference。

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