基礎的WCF回調機制並不能闡明客戶端與服務之間交互的本質。雙向回調的規範使用能夠經過事件來完成。客戶端發生的相關事項均可以經過事件通知客戶端或者多個客戶端。事件可能源於直接的客戶端調用,也可能來源於服務監聽器。激活事件的服務稱爲發佈者,而接受事件答得客戶端則稱爲訂閱者。以下圖所示: spa
與回調操做相比,WCF更重視對事件的運做。從本質講,事件表明了發佈者與訂閱者之間更加鬆散的關係,他優於客戶端和服務之間的關係。處理事件時,服務一般會爲多個訂閱客戶端發佈一樣的事件。發佈者通常不會考慮訂閱者的回調數順序,也不會考慮訂閱者在處理事件時可能出現的錯誤。全部的發佈者都直到它應該將事件傳遞給訂閱者。若是事件出現問題,服務對此也一籌莫展。此外,服務並不關心訂閱者返回的結果。所以,事件處理操做的返回值應該爲void,而不須要返回值,於是應該被標記爲單向操做。建議將事件分解爲單獨的回調契約,而不要在相同的契約中將事件和常規的回調混在一塊兒。 code
public interface IMyEvent { [OperationContract(IsOneWay=true)] void OnEvent1(); [OperationContract(IsOneWay=true)] void OnEvent2(int number); [OperationContract(IsOneWay=true)] void OnEvent3(int number,string text); }在訂閱端,即便使用了單向回調操做,事件處理方法的實現也應該是執行週期較短的操做。其緣由有二:第一,若是須要發佈大量的事件,以致於超過了訂閱者的能力,且因爲隊列正在處理前一個事件,沒法將回調放入隊列中,就會致使發佈者被阻塞。阻塞了發佈者就會阻止事件到達其它的訂閱者;第二,若是存在大量的事件訂閱者,每一個訂閱者所累加起來的處理事件就會操做發佈者的超時值。blog
發佈者能夠爲它的契約添加專門的操做,容許客戶端顯式地訂閱事件或取消對事件的訂閱。若是發佈者支持多個事件類型,也能夠容許訂閱者選擇它們但願訂閱或取消訂閱的事件。隊列
服務如何從內部管理訂閱者列表及選擇它們的參數,徹底屬於服務端的實現細節,不會影響到客戶端。發佈者可使用.NET委託管理訂閱者列表和發佈者自身的行爲,也可使用泛型進行管理。事件
[Flags] public enum EvenType { Event1 = 1, Event2 = 2, Event3 = 4, AllEvents = Event1 | Event2 | Event3 } [ServiceContract(CallbackContract = typeof(IMyEventsCallback))] public interface IMyContract { [OperationContract] void DoSomething(); [OperationContract] void Subscribe(EvenType mask); [OperationContract] void UnSubscribe(EvenType mask); } [ServiceBehavior(InstanceContextMode=InstanceContextMode.PerCall)] public class MyPublisher : IMyContract { private static Action m_Event1 = delegate { }; private static Action<int> m_Event2 = delegate { }; private static Action<int, string> m_Event3 = delegate { }; public void Subscribe(EvenType mask) { IMyEventsCallback subscriber = OperationContext.Current.GetCallbackChannel<IMyEventsCallback>(); if ((mask & EvenType.Event1) == EvenType.Event1) { m_Event1 += subscriber.OnEvent1; } if ((mask & EvenType.Event2) == EvenType.Event2) { m_Event2 += subscriber.OnEvent2; } if ((mask & EvenType.Event3) == EvenType.Event3) { m_Event3 += subscriber.OnEvent3; } } public void UnSubscribe(EvenType mask) { IMyEventsCallback subscriber = OperationContext.Current.GetCallbackChannel<IMyEventsCallback>(); if ((mask & EvenType.Event1) == EvenType.Event1) { m_Event1 -= subscriber.OnEvent1; } if ((mask & EvenType.Event2) == EvenType.Event2) { m_Event2 -= subscriber.OnEvent2; } if ((mask & EvenType.Event3) == EvenType.Event3) { m_Event3 -= subscriber.OnEvent3; } } public static void FireEvent(EvenType eventType) { switch (eventType) { case EvenType.Event1: { m_Event1(); return; } case EvenType.Event2: { m_Event2(42); return; } case EvenType.Event3: { m_Event3(42, "Hello"); return; } default: { throw new InvalidOperationException("Unkown event type"); } } } public void DoSomething() { throw new NotImplementedException(); } }服務契約IMyContract定義了Subscribe()和UnSubscribe()方法。這些方法接受一個枚舉類型EventType,枚舉類型的字段均被設置爲2的指數。這就能使訂閱客戶端經過枚舉值掩碼標識事件類型是訂閱仍是取消訂閱。例如,訂閱Event1和Event3,但不訂閱Event2訂閱者會調用以下的Subsctibe()方法:get
class Program { static void Main(string[] args) { IMyEventCallback subscriber = new MySubscriber(); InstanceContext context = new InstanceContext(subscriber); MyContractClient proxy = new MyContractClient(context); proxy.Subscribe(Service.EvenType.Event1 | Service.EvenType.Event3); proxy.DoSomething(); proxy.Close(); Console.Read(); } } public class MySubscriber : IMyEventCallback { public void OnEvent1() { Console.WriteLine("OnEvent1 Action"); } public void OnEvent2(int number) { Console.WriteLine("OnEvent2 Action,number={0}",number); } public void OnEvent3(int number, string text) { Console.WriteLine("OnEvent3 Action,number={0},text={1}", number,text); } }MyPublisher內部維持了三個靜態委託,每個委託對應一個事件類型。string
Subscribe()與UnSubscribe()方法都檢查;額傳入參數EventType值,從對應的委託中添加或移除訂閱者的回調。爲了觸發事件,MyPublisher提供了靜態方法FireEvent()。他根據EventType值判斷應該觸發哪個事件,而後調用對應的委託。it
示例代碼:下載io