WCF InstanceContextMode 決定服務實例的建立時機、生命週期與共享範圍,是服務設計的核心決策之一。三種模式各有優劣,需根據業務特性選擇。
| 模式 | 建立時機 | 銷毀時機 | 狀態共享範圍 | 典型用途 |
|---|---|---|---|---|
| PerCall | 每次呼叫前 | 呼叫結束後 | 無 (每呼叫獨立) | 無狀態、高併發、可擴展服務 |
| PerSession (預設) | Session 建立時 | Session 結束時 | Session 內共享 | 有狀態商務流程、購物車、精靈嚮導 |
| Single | 服務啟動時 (首次呼叫) | 服務關閉時 | 全域共享 (所有呼叫者) | 快取、計數器、單例資源管理 |
[ServiceContract]
public interface IPerCallMode {
[OperationContract] int Counter();
}
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
public class PerCallMode : IPerCallMode {
private int _counter;
public int Counter() => ++_counter; // 每次呼叫永遠回傳 1
}
[ServiceContract]
public interface IPerSessionMode {
[OperationContract] int Counter();
}
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)]
public class PerSessionMode : IPerSessionMode {
private int _counter;
public int Counter() => ++_counter; // 同一 Session 遞增
}
[ServiceContract]
public interface ISingleMode {
[OperationContract] int Counter();
}
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
public class SingleMode : ISingleMode {
private int _counter;
public int Counter() => ++_counter; // 全域遞增,所有客戶端共享
}
<service name="WCF.ServiceLib.InstanceMode.PerCallMode">
<endpoint address="" binding="basicHttpBinding" contract="WCF.ServiceLib.InstanceMode.IPerCallMode" />
</service>
<service name="WCF.ServiceLib.InstanceMode.SingleMode">
<endpoint address="" binding="basicHttpBinding" contract="WCF.ServiceLib.InstanceMode.ISingleMode" />
</service>
<service name="WCF.ServiceLib.InstanceMode.PerSessionMode">
<endpoint address="" binding="wsHttpBinding"
contract="WCF.ServiceLib.InstanceMode.IPerSessionMode"
bindingConfiguration="PerSessionModeBindingConfiguration" />
</service>
<wsHttpBinding>
<binding name="PerSessionModeBindingConfiguration">
<reliableSession enabled="true" />
<security>
<message establishSecurityContext="true" />
</security>
</binding>
</wsHttpBinding>
basicHttpBinding 不支援會話,wsHttpBinding 需啟用 reliableSession 與安全內容才能建立 Session。
var proxy = new PerCallModeClient();
proxy.Counter(); // 1
proxy.Counter(); // 1
proxy.Close();
if (Session["proxy"] == null)
Session["proxy"] = new PerSessionModeClient();
var proxy = Session["proxy"] as PerSessionModeClient;
proxy.Counter(); // 1
proxy.Counter(); // 2
proxy.Counter(); // 3
var proxy = new SingleModeClient();
proxy.Counter(); // 1 (第一個用戶)
proxy.Counter(); // 2 (同用戶)
var proxy2 = new SingleModeClient();
proxy2.Counter(); // 3 (不同用戶,共享同一實例)
| InstanceContextMode | 建議 ConcurrencyMode | 說明 |
|---|---|---|
| PerCall | Multiple | 每呼叫新實例,天然無併發問題 |
| PerSession | Single/Reentrant | Session 內單執行緒,避免同步複雜度 |
| Single | Multiple | 需自行實作同步 (Mutex, lock 等) |
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Multiple)]
public class SingleMode : ISingleMode {
// 使用 ThreadLocal 或 ConcurrentDictionary 隔離各呼叫者狀態
private ConcurrentDictionary _counters = new ConcurrentDictionary();
public int Counter(string clientId) {
return _counters.AddOrUpdate(clientId, 1, (k, v) => v + 1);
}
}
理解實例模式的生命週期與狀態共享語意,是設計高效能、可維護 WCF 服務的基礎功力。