I’d like to talk to you for a moment about my man The Event Aggregator. (Homeboy’s been tossin’ events for years!) Martin Fowler defines the Event Aggregator as:

“Channel events from multiple objects into a single object to simplify registration for clients.” - Martin Fowler

But he’s so much more then that! This pattern comes in handy in state-ful environments, so.. hello WinForms/WPF. Sorry ASP.NET!

What this guy does is creates a single channel to find out what’s going on and to raise events. Through the event aggregator you can subscribe to known events in the system as well as raise known events in the system. Some examples for system wide events are Save, Loading, Shutdown. You can also create concepts for layer specific event aggregators that target certain layers of a system. For example UI only.

 1 public class ApplicationEvents {
 2         private static readonly IEventAggregator aggregator;
 3         public static readonly IEvent Save = new EventRaiser( "Save" );
 4         public static readonly IEvent Loading = new EventRaiser( "Loading" );
 5 
 6         static ApplicationEvents( ) {
 7             aggregator = new EventAggregator( );
 8             aggregator.Register( Save );
 9             aggregator.Register( Loading );
10         }
11 
12         public static void Raise( IEvent eventToRaise ) {
13             aggregator.RaiseEvent( eventToRaise );
14         }
15 
16         public static void SubscribeTo( IEvent eventToSubscribeTo, EventHandler handler ) {
17             aggregator.AddHandler( eventToSubscribeTo.Name, handler );
18         }
19     }

The ApplicationEvents becomes the central point for raising and subscribing to known application wide events. Type can be coupled to the aggregator but not to each other. Let’s start with the concept of a named event. An event is really nothing more then a delegate. In .NET there are 2 delegate signatures that are used for all events. The generic and non generic version of the EventHandler delegate. (Read More… ) A named event is an event with a name. He might look something like this guy?

internal class EventRaiser : IEvent {
        public EventRaiser( string name ) {
            _name = name;
            _handlers = new List< EventHandler >( );
        }

        public string Name {
            get { return _name; }
        }

        public void Add( EventHandler handler ) {
            _handlers.Add( handler );
        }

        public void Raise( ) {
            Raise( null, null );
        }

        public void Raise( object sender, EventArgs data ) {
            foreach ( EventHandler handler in _handlers ) {
                if ( handler != null ) {
                    handler( sender, data );
                }
            }            
        }

        private readonly string _name;
        private readonly IList< EventHandler > _handlers;
    }

Underneath the hood it’s the aggregator that’s maintaining references to all the types that have subscribed to events.

 1 public class EventAggregator : IEventAggregator {
 2         public EventAggregator( ) {
 3             _events = new Dictionary< string, IEvent >( );
 4         }
 5 
 6         public void Register( string eventName ) {
 7             Register( new EventRaiser( eventName ) );
 8         }
 9 
10         public void Register( IEvent eventToAdd ) {
11             EnsureEventHasntBeenRegistered( eventToAdd );
12             _events.Add( eventToAdd.Name, eventToAdd );
13         }
14 
15         public void AddHandler( string eventName, EventHandler handler ) {
16             RetrieveEvent( eventName ).Add( handler );
17         }
18 
19         public void RaiseEvent( string withEventName ) {
20             RaiseEvent< EventArgs >( withEventName, null, EventArgs.Empty );
21         }
22 
23         public void RaiseEvent( IEvent eventToRaise ) {
24             RaiseEvent< EventArgs >( eventToRaise.Name, null, EventArgs.Empty );
25         }
26 
27         public void RaiseEvent< T >( string withEventName, object sender, T data ) where T : EventArgs {
28             RetrieveEvent( withEventName ).Raise( sender, data );
29         }
30 
31         private IEvent RetrieveEvent( string eventName ) {
32             if ( _events.ContainsKey( eventName ) ) {
33                 return _events[ eventName ];
34             }
35             throw new ArgumentNullException( eventName, "The Event Has Not Been Registered." );
36         }
37 
38         private void EnsureEventHasntBeenRegistered( IEvent eventToAdd ) {
39             if ( _events.ContainsKey( eventToAdd.Name ) ) {
40                 throw new ArgumentException( "Event name has already been registered", eventToAdd.Name );
41             }
42         }
43 
44         private readonly IDictionary< string, IEvent > _events;
45     }

The concept is similar to Juval Lowy’s EventHelper class defined in…

Programming .NET Components, 2nd Edition by Juval Lowy Read more about this title…

comments powered by Disqus