1. PreCall
在 PreCall 模式下,即便使用同一個代理對象,也會爲每次調用創建一個服務實例。調用結束後,服務實例被立即釋放(非垃圾回收)。對於不支持 Session 的 Binding,如 BasicHttpBinding,其缺省行爲就是 PreCall。
[ServiceContract] public interface IMyService { [OperationContract] void Test(); } [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)] public class MyServie : IMyService, IDisposable { public MyServie() { Console.WriteLine("Constructor:{0}", this.GetHashCode()); } [OperationBehavior] public void Test() { Console.WriteLine("Test:{0}", OperationContext.Current.SessionId); } public void Dispose() { Console.WriteLine("Dispose"); } } public class WcfTest { public static void Test() { AppDomain.CreateDomain("Server").DoCallBack(delegate { ServiceHost host = new ServiceHost(typeof(MyServie), new Uri("http://localhost:8080/MyService")); host.AddServiceEndpoint(typeof(IMyService), new WSHttpBinding(), ""); host.Open(); }); //----------------------- IMyService channel = ChannelFactory<IMyService>.CreateChannel(new WSHttpBinding(), new EndpointAddress("http://localhost:8080/MyService")); using (channel as IDisposable) { channel.Test(); channel.Test(); } } }
輸出:
Constructor:30136159
Test:urn:uuid:df549447-52ba-4c54-9432-31a7a533d9b4
Dispose
Constructor:41153804
Test:urn:uuid:df549447-52ba-4c54-9432-31a7a533d9b4
Dispose
2. PreSession
PreSession 模式需要綁定到支持 Session 的 Binding 對象。在客戶端代理觸發終止操作前,WCF 爲每個客戶端維持同一個服務對象,因此 PreSession 模式可用來保持調用狀態。也正因爲如此,PreSession 在大併發服務上使用時要非常小心,避免造成服務器過度負擔。雖然支持 Session 的 Binding 對象缺省就會啓用 PreSession 模式,但依然建議你強制指定 SessionMode.Required 和 InstanceContextMode.PerSession。
[ServiceContract(SessionMode = SessionMode.Required)] public interface IMyService { [OperationContract] void Test(); } [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)] public class MyServie : IMyService, IDisposable { public MyServie() { Console.WriteLine("Constructor:{0}", this.GetHashCode()); } [OperationBehavior] public void Test() { Console.WriteLine("Test:{0}", OperationContext.Current.SessionId); } public void Dispose() { Console.WriteLine("Dispose"); } } public class WcfTest { public static void Test() { AppDomain.CreateDomain("Server").DoCallBack(delegate { ServiceHost host = new ServiceHost(typeof(MyServie), new Uri("http://localhost:8080/MyService")); host.AddServiceEndpoint(typeof(IMyService), new WSHttpBinding(), ""); host.Open(); }); //----------------------- IMyService channel = ChannelFactory<IMyService>.CreateChannel(new WSHttpBinding(), new EndpointAddress("http://localhost:8080/MyService")); using (channel as IDisposable) { channel.Test(); channel.Test(); } } }
輸出:
Constructor:30136159
Test:urn:uuid:2f01b61d-40c6-4f1b-a4d6-4f4bc3e8847a
Test:urn:uuid:2f01b61d-40c6-4f1b-a4d6-4f4bc3e8847a
Dispose
3. Single
一如其名,服務器會在啓動時,創建一個唯一(Singleton)的服務對象。這個對象爲所有的客戶端服務,並不會隨客戶端終止而釋放。
[ServiceContract] public interface IMyService { [OperationContract] void Test(); } [ServiceBehavior(InstanceContextMode=InstanceContextMode.Single)] public class MyServie : IMyService, IDisposable { public MyServie() { Console.WriteLine("Constructor:{0}; {1}", DateTime.Now, this.GetHashCode()); } [OperationBehavior] public void Test() { Console.WriteLine("Test:{0}; {1}", DateTime.Now, OperationContext.Current.SessionId); } public void Dispose() { Console.WriteLine("Dispose:{0}", DateTime.Now); } } public class WcfTest { public static void Test() { AppDomain.CreateDomain("Server").DoCallBack(delegate { ServiceHost host = new ServiceHost(typeof(MyServie), new Uri("http://localhost:8080/MyService")); host.AddServiceEndpoint(typeof(IMyService), new BasicHttpBinding(), ""); host.Open(); }); //----------------------- for (int i = 0; i < 2; i++) { IMyService channel = ChannelFactory<IMyService>.CreateChannel(new BasicHttpBinding(), new EndpointAddress("http://localhost:8080/MyService")); using (channel as IDisposable) { channel.Test(); channel.Test(); } } } }
輸出:
Constructor:2007-4-17 17:31:01; 63238509
Test:2007-4-17 17:31:03;
Test:2007-4-17 17:31:03;
Test:2007-4-17 17:31:03;
Test:2007-4-17 17:31:03;
還有另外一種方式來啓動 Single ServiceHost。
AppDomain.CreateDomain("Server").DoCallBack(delegate { MyServie service = new MyServie(); ServiceHost host = new ServiceHost(service, new Uri("http://localhost:8080/MyService")); host.AddServiceEndpoint(typeof(IMyService), new BasicHttpBinding(), ""); host.Open(); });
此方式最大的好處是允許我們使用非默認構造,除此之外,和上面的例子並沒有什麼區別。
需要特別注意的是,缺省情況下,Single 會對服務方法進行併發控制。也就是說,多個客戶端需要排隊等待,直到排在前面的其他客戶端調用完成後才能繼續。看下面的例子。
[ServiceContract] public interface IMyService { [OperationContract] void Test(); } [ServiceBehavior(InstanceContextMode=InstanceContextMode.Single)] public class MyServie : IMyService, IDisposable { public MyServie() { Console.WriteLine("Constructor:{0}", this.GetHashCode()); } [OperationBehavior] public void Test() { Console.WriteLine("Test:{0}", OperationContext.Current.SessionId); Thread.Sleep(2000); Console.WriteLine("Test End:{0}", OperationContext.Current.SessionId); } public void Dispose() { Console.WriteLine("Dispose"); } } public class WcfTest { public static void Test() { AppDomain.CreateDomain("Server").DoCallBack(delegate { ServiceHost host = new ServiceHost(typeof(MyServie), new Uri("http://localhost:8080/MyService")); host.AddServiceEndpoint(typeof(IMyService), new WSHttpBinding(), ""); host.Open(); }); //----------------------- for (int i = 0; i < 2; i++) { new Thread(delegate() { IMyService channel = ChannelFactory<IMyService>.CreateChannel(new WSHttpBinding(), new EndpointAddress("http://localhost:8080/MyService")); using (channel as IDisposable) { while (true) { channel.Test(); } } }).Start(); } } }
輸出:
Constructor:63238509
Test:urn:uuid:d613cfce-d454-40c9-9f4d-62b30a93ff27
Test End:urn:uuid:d613cfce-d454-40c9-9f4d-62b30a93ff27
Test:urn:uuid:313de31e-96b8-47c2-8cf3-7ae743236dfc
Test End:urn:uuid:313de31e-96b8-47c2-8cf3-7ae743236dfc
Test:urn:uuid:d613cfce-d454-40c9-9f4d-62b30a93ff27
Test End:urn:uuid:d613cfce-d454-40c9-9f4d-62b30a93ff27
Test:urn:uuid:313de31e-96b8-47c2-8cf3-7ae743236dfc
Test End:urn:uuid:313de31e-96b8-47c2-8cf3-7ae743236dfc
Test:urn:uuid:d613cfce-d454-40c9-9f4d-62b30a93ff27
Test End:urn:uuid:d613cfce-d454-40c9-9f4d-62b30a93ff27
Test:urn:uuid:313de31e-96b8-47c2-8cf3-7ae743236dfc
Test End:urn:uuid:313de31e-96b8-47c2-8cf3-7ae743236dfc
...
我們可以通過修改併發模式(ConcurrencyMode)來改變這種行爲,但需要自己維護多線程安全。
[ServiceBehavior(InstanceContextMode=InstanceContextMode.Single, ConcurrencyMode=ConcurrencyMode.Multiple)] public class MyServie : IMyService, IDisposable { //... }
輸出:
Constructor:10261382
Test:urn:uuid:2bdac798-f774-40f2-b8f9-58de8d599d3c
Test:urn:uuid:9a328d0e-8df7-4885-94ed-e04ceedc5a09
Test End:urn:uuid:2bdac798-f774-40f2-b8f9-58de8d599d3c
Test:urn:uuid:2bdac798-f774-40f2-b8f9-58de8d599d3c
Test End:urn:uuid:9a328d0e-8df7-4885-94ed-e04ceedc5a09
Test:urn:uuid:9a328d0e-8df7-4885-94ed-e04ceedc5a09
Test End:urn:uuid:2bdac798-f774-40f2-b8f9-58de8d599d3c
Test:urn:uuid:2bdac798-f774-40f2-b8f9-58de8d599d3c
Test End:urn:uuid:9a328d0e-8df7-4885-94ed-e04ceedc5a09
Test:urn:uuid:9a328d0e-8df7-4885-94ed-e04ceedc5a09
Test End:urn:uuid:2bdac798-f774-40f2-b8f9-58de8d599d3c
Test:urn:uuid:2bdac798-f774-40f2-b8f9-58de8d599d3c
Test End:urn:uuid:9a328d0e-8df7-4885-94ed-e04ceedc5a09
Test:urn:uuid:9a328d0e-8df7-4885-94ed-e04ceedc5a09