開發者常有此疑問:基礎類方法已是 `static`,Web Service 代理類 `TestSrv` 只是薄封裝,為什麼不直接把 `GetType()` 也標記為 `static`,調用時省去 `new TestSrv()` 實例化開銷?
// 基礎類
public class Base {
public static Type GetType() { ... }
}
// Web Service 代理類
[WebService(Namespace = "...")]
public class TestSrv : System.Web.Services.WebService {
// 為什麼不能這樣寫?
// [WebMethod]
// public static Type GetType() => Base.GetType();
[WebMethod]
public Type GetType() => Base.GetType(); // 必須是實例方法
}
ASP.NET ASMX Web Service(舊版 XML Web Services)基於 每請求一實例 模型:
[WebMethod]` 的 **實例方法** 並調用。IDisposable` 則調用 `Dispose`)。靜態方法不屬於任何實例,無法被此生命週期管理機制發現與調用。若強行標記 `static`,編譯雖通過,但運行時會拋出 `InvalidOperationException`:「Web 服務方法必須是實例方法」。
| 維度 | 實例方法 | 靜態方法 |
|---|---|---|
| 狀態管理 | 可訪問 `this.Context`、`Session`、`Application`、`User` 等內建屬性 | 無 `this` 參考,無法直接訪問上下文 |
| 依賴注入 | 支援構造函數注入(配合 `IHttpHandlerFactory` 或自定義工廠) | 無構造函數,難以注入作用域服務 |
| 事務/攔截 | 可配合 `SoapExtension`、`IHttpModule` 進行 AOP 攔截 | 靜態調用繞過管道,難以攔截 |
| 線程安全 | 每請求新實例,天然隔離字段狀態 | 共享靜態字段需自行鎖定,風險高 |
| 測試性 | 易於 Mock/Stub 實例 | 靜態方法難以 Mock,單元測試受阻 |
你的直覺沒錯:**業務邏輯應盡量無狀態、靜態化**。最佳實踐是分層:
// 1. 靜態核心邏輯層(純函數、易測試、可復用)
public static class BusinessLogic {
public static Type ResolveType(string input) { ... }
}
// 2. Web Service 適配層(薄實例化外殼)
[WebService]
public class TestSrv : WebService {
[WebMethod]
public Type GetType(string input) {
// 僅負責:參數驗證、異常轉換、調用核心邏輯、返回 DTO
return BusinessLogic.ResolveType(input);
}
}
// 3. 客戶端調用(自動生成代理類,調用實例方法)
var proxy = new TestSrv();
Type result = proxy.GetType("param");
ASMX 屬於 .NET Framework 早期技術(2002~),已進入維護模式。新專案建議:
app.MapGet("/type", () => BusinessLogic.ResolveType(...))`,委託可捕獲靜態/單例服務,零樣板代碼。Web Service 方法**必須是實例方法**,這是由 ASP.NET ASMX 運行時的「每請求一實例」管道架構決定的。將核心邏輯提取為 `static` 類或注入服務,Web Service 類僅作為薄適配層,既滿足框架約束,又保持代碼乾淨、可測、可遷移。