基础的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