Section 5.4. Events


5.4. Events

The basic WCF callback mechanism does not indicate anything about the nature of the interaction between the client and the service. They could be equal peers in a commutative interaction, each calling and receiving calls from the other.

However, the canonical use for duplex callbacks is with events. Events allow the client or clients to be notified about something that occurred on the service side. The event may result from a direct client call, or it may be the result of something the service monitors. The service firing the event is called the publisher, and the client receiving the event is called the subscriber. Events are a required feature in almost any type of application, as shown in Figure 5-2.

Figure 5-2. A publishing service can fire events at multiple subscribing clients


While events in WCF are noting more than callback operations, by their very nature events usually imply a looser relationship between the publisher and the subscriber, compared with the relationship between a client and a service. When dealing with events, the service typically publishes the same event to multiple subscribing clients. The publisher often does not care about the order of the invocation of the subscribers. The publishing service usually does not care about errors the subscribers might have while processing the events. All the publisher knows is that it should deliver the event to the subscribers. If they have a problem with the event, there is nothing the service can do about it anyway. In addition, the service does not care about returned results from the subscribers. Consequently, event-handling operations should have a void return type, should not have any outgoing parameters, and should be marked as one-way. I also recommend factoring the events to a separate callback contract, and not mixing events with regular callbacks on the same contract:

 interface IMyEvents {    [OperationContract(IsOneWay = true)]    void OnEvent1( );    [OperationContract(IsOneWay = true)]    void OnEvent2(int number);    [OperationContract(IsOneWay = true)]    void OnEvent3(int number,string text); } 

On the subscriber side, even when using one-way callback operations, the implementation of the event handling methods should be of short duration. The reason is that if there is a large volume of events to publish, the publisher may get blocked if a subscriber has maxed out its ability to queue up callbacks because it was still processing the previous events. Blocking the publisher may prevent the event from reaching other subscribers in a timely manner. The publisher may add dedicated operations to its contract, allowing clients to explicitly subscribe to or unsubscribe from the events. If the publisher supports multiple event types, it may want to allow the subscribers to choose exactly which event they want to subscribe to or unsubscribe from.

How the service internally goes about managing the subscribers list and their preferences is completely a service-side implementation detail that should not affect the clients.

The publisher can even use .NET delegates to manage the list of subscribers and the publishing act itself. Example 5-16 demonstrates this technique as well as the other design considerations discussed so far.

Example 5-16. Events management using delegates

 enum EventType {    Event1 = 1,    Event2 = 2,    Event3 = 4,    AllEvents = Event1|Event2|Event3 } [ServiceContract(CallbackContract = typeof(IMyEvents))] interface IMyContract {    [OperationContract]    void DoSomething( );    [OperationContract]    void Subscribe(EventType mask);    [OperationContract]    void Unsubscribe(EventType mask); } [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)] class MyPublisher : IMyContract {    static GenericEventHandler m_Event1                   = delegate{};    static GenericEventHandler<int> m_Event2        = delegate{};    static GenericEventHandler<int,string> m_Event3 = delegate{};    public void Subscribe(EventType mask)    {       IMyEvents subscriber =  OperationContext.Current.                                                    GetCallbackChannel<IMyEvents>( );       if((mask & EventType.Event1) == EventType.Event1)       {          m_Event1 += subscriber.OnEvent1;       }       if((mask & EventType.Event2) == EventType.Event2)       {          m_Event2 += subscriber.OnEvent2;       }       if((mask & EventType.Event3) == EventType.Event3)       {          m_Event3 += subscriber.OnEvent3;       }    }    public void Unsubscribe(EventType mask)    {       //Similar to Subscribe( ) but uses -=    }    public static void FireEvent(EventType eventType)    {       switch(eventType)       {          case EventType.Event1:          {             m_Event1( );             return;          }          case EventType.Event2:          {             m_Event2(42);             return;          }          case EventType.Event3:          {             m_Event3(42,"Hello");             return;          }          default:          {             throw new InvalidOperationException("Unknown event type");          }       }    }    public void DoSomething( )    {...} } 

The service contract IMyContract defines the Subscribe( ) and Unsubscribe( ) methods. These methods take an enum of the type EventType, whose individual fields are set to integer powers of 2. This enables the subscribing client to combine the values into a mask indicating the types of event it wants to subscribe to or unsubscribe from. For example, to subscribe to Event1 and Event3 but not Event2, the subscriber would call like this:

 class MySubscriber : IMyEvents {    void OnEvent1( )    {...}    void OnEvent2(int number)    {...}    void OnEvent2(int number,string text)    {...} } IMyEvents subscriber = new MySubscriber( ); InstanceContext context = new InstanceContext(subscriber); MyContractClient proxy = new MyContractClient(context); proxy.Subscribe(EventType.Event1|EventType.Event3); 

Internally, MyPublisher maintains three static delegates; each corresponds to an event type. The delegates are all of the generic delegate type GenericDelegate:

 public delegate void GenericEventHandler( ); public delegate void GenericEventHandler<T>(T t); public delegate void GenericEventHandler<T,U>(T t,U u); public delegate void GenericEventHandler<T,U,V>(T t,U u,V v); public delegate void GenericEventHandler<T,U,V,W>(T t,U u,V v,W w); public delegate void GenericEventHandler<T,U,V,W,X>(T t,U u,V v,W w,X x); public delegate void GenericEventHandler<T,U,V,W,X,Y>(T t,U u,V v,W w,X x,Y y); 

GenericDelegate allows you to literally express any event-handling signature.

For more information about delegates and GenericDelegate, see Chapter 6 in my book Programming .NET Components (O'Reilly).


Both the Subscribe( ) and Unsubscribe( ) methods check the supplied EventType value, and add or remove the subscriber's callback to the corresponding delegate. To fire an event, MyPublisher offers the static FireEvent( ) method. FireEvent( ) accepts which event to fire and invokes the corresponding delegate.

Again, the fact that the MyPublisher service uses delegates is purely an implementation detail simplifying the events lookup. The service could have used a linked list, although with more complex code.

Appendix B presents a framework for supporting a better design approach for events called publish-subscriber.





Programming WCF Services
Programming WCF Services
ISBN: 0596526997
EAN: 2147483647
Year: 2004
Pages: 148
Authors: Juval Lowy

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net