Sponsored By Aspose - File Format APIs for .NET

Aspose are the market leader of .NET APIs for file business formats – natively work with DOCX, XLSX, PPT, PDF, MSG, MPP, images formats and many more!

Build your own CAB: Extensible Pub/Sub Event Aggregator with Generics

There’s been a bit of chatter today on the altdotnet list about whether to use the CAB, another framework, or roll your own.  It seemed like a good time to get off my duff and start writing some new installments in the Build your own CAB series.  When I wrote an earlier installment on the Event Aggregator pattern, Udi Dahan left a suggestion in a comment for a more general usage of the pattern.  Lo and behold, I found a reason to apply his suggestion to StoryTeller and came away pretty happy with the results.  Coincidentally, CodeBetter-mate Glenn Block is writing some posts on the very same Event Aggregator pattern with a similar design.  I’ll use this to pressure him into finishing his series;-)

 

What’s the Problem?

Ideally a single screen is basically self-contained.  The View and Presenter talk to each other, the Presenter talks to some sort of service layer, and that’s that.  But like any idyllic spot, the real world will intrude.  A screen will often need to react to actions or events taking place in other parts of the UI.  You may have a feature that broadcasts updated data from the server to the client that would need to update the display of a screen or the state of the UI.  Some action taking place in one screen might affect other parts of the UI, and it can easily be more than one element of the UI. 

At this point we’re facing a situation where a lot of screen elements, Views, and pieces of the application shell are interrelated and need to be synchronized.  The problem we have right off the bat is how to get all of these pieces talking together in the first place.  The next problem is how to avoid making the UI code a tangle of references from all these different parts trying to get a reference to each other just to talk to one another.  If you’re not careful, you can create some nasty spaghetti code connecting all the disparate parts of the UI.

Once again, we’re going to reach for a Publish/Subscribe method for routing events from the origin of the event.  To eliminate undesirable coupling between the event publishers and the event listeners we’re going to route all the notifications through a central messaging hub called an Event Aggregator.  If a publisher can simply send a message to the Event Aggregator hub, the hub will relay that message to the proper listeners.  New listeners can be added later to the Event Aggregator without the publishers being affected. 

 

The Scenario

Here’s my scenario from StoryTeller.  I have a pane on the left that acts in the same capacity as the solution explorer from Visual Studio to show all of the open testing projects and provide access to all of the Tests and Test Suites.  When a user adds a new testing project to the working set, another part of the UI called the FixtureExplorer needs to update its display to reflect the available Fixture classes for each project.  This isn’t coded yet, but when a testing project is removed, I need all open tabs that are related to that project to be closed and removed from the UI.   

The three main challenges I faced were

  1. How does the Event Aggregator know which listeners are interested in a given message?
  2. How to get a reference to the Event Aggregator?
  3. How to register the listeners?

 

Message Routing with Generics

I didn’t want my listeners to have to implement a generic handler method that would have to analyze a message coming in to decide what to do.  For the sake of easier testing and more explicit code, I wanted a strongly typed method as part of a listeners public API that handled a specific message.  Furthermore, to simplify the listeners, I want the Event Aggregator to be responsible for deciding what messages a given listener is interested in.  And oh by the way, I have to be able to add new message types without changing the Event Aggregator.

In the end, the answer was really pretty simple.  The listeners just need to implement the IListener<T> interface, where T is the type of a message.  The Handle() method is where a listener reacts to an event message. 

    public interface IListener<T>
    {
        void Handle(T subject);
    }

For a concrete example of a listener, I have a class called FixtureExplorerPresenter that needs to listen to several events related to testing projects. To register as a listener, it just has to implement IListener for each event:

    public class FixtureExplorerPresenter : IFixtureExplorerPresenter,
                                            IListener<SystemLoaded>,
                                            IListener<SystemActivated>,
                                            IListener<SystemDeactivated>,
                                            IListener<SystemReloaded>,
                                            IListener<SystemRemoved>

The listening methods look something like this:

        // This is the implementation of the IListener<SystemLoaded> interface
        // The Event Aggregator will call this method when a SystemLoaded message
        // is published
        public void Handle(SystemLoaded subject)
        {
            ISystemUnderTest system = subject.SystemUnderTest;
 
            ReloadSystem(system);
 
            if (isSystemActive(system))
            {
                loadNode(system);
            }
        }

The details of what it’s doing isn’t that interesting, but I want to point out how easy it was in a listener to subscribe to an event.  There’s no generic method that receives an event and then has to run it through a switch statement to figure out what to do.  FixtureExplorerPresenter just gets a SystemLoaded event and works accordingly.  In my unit tests for FixtureExplorerPresenter I can just walk right up to the class and call Handle(SystemLoaded) without having to set up anything of the rest of the system.

        [Test]
        public void SystemLoadedReloadsTheFixturesForThatSystemAndResetsTheViewIfThisIsTheActiveSystem()
        {
            FixtureExplorerPresenter presenter = _mocks.CreateMock<FixtureExplorerPresenter>(_view, _writer);
 
            ISystemUnderTest system = ObjectMother.SystemUnderTest();
            presenter.ReloadSystem(system);
 
            Expect.Call(_view.ActiveSystemName).Return(system.Name);
            SystemFixturesNode theNode = new SystemFixturesNode(system, new FixtureToken[0]);
            Expect.Call(presenter.GetNode(system)).Return(theNode);
            _view.DisplayFixturesNode(theNode);
 
 
            _mocks.ReplayAll();
            presenter.Handle(new SystemLoaded(system));
            _mocks.VerifyAll();
        }

Creating the listeners was easy enough because all a listener had to do was implement IListener<T> interfaces for all the events it needed to listen to.  We’ve still got an outstanding responsibility for publishing events to the correct listeners, so let’s turn our attention to the hub in our publish/subscribe eventing model.

In StoryTeller, events are published to the rest of the system by getting a reference to an instance of this interface (highly elided) shown below;

    public interface ITestEventPublisher
    {
        ...
 
        void PublishEvent<T>(T subject);
        void AddListener(object listener);
        void RemoveListener(object listener);
    }

A client of the ITestEventPublisher interface just needs to call the PublishEvent<T>(T message) method to broadcast a message to the rest of the application.  In one of the samples above I showed some code in the FixtureExplorerPresenter class that responded to a “SystemLoaded” event to update part of the screen.  Here’s the other side of the publish/subscribe.  Another part of the system called ApplicationController is responsible for loading new testing Projects into the test explorer.  The ApplicationController.AddProject() method shown below needs to tell FixtureExplorerPresenter and the other listeners that a new “SystemUnderTest” has been loaded into the UI.  Instead of calling FixtureExplorerPresenter directly and causing the n^2 messaging anti-pattern, it sends a SystemLoaded message to an ITestEventPublisher.

        public virtual void AddProject(IProject project)
        {
            ISystemUnderTest system = project.BuildSystem();
            _hierarchyNode.AddProject(project, system);
 
            // _publisher is an instance of the ITestEventPublisher interface
            _publisher.PublishEvent(new SystemLoaded(system));
        }

The concrete method PublishEvent() is shown below: 

        public void PublishEvent<T>(T subject)
        {
            // _listeners is just an ArrayList of all the Listeners 
            foreach (object listener in _listeners)
            {
                // Determine if a Listener handles the message of type T
                // by trying to cast it
                IListener<T> receiver = listener as IListener<T>;
                if (receiver != null)
                {
                    // I'm using SyncronizationContext to handle moving processing
                    // from a background thread to the UI thread without having 
                    // to worry about it in the View or Presenter
                    _context.Send(delegate { receiver.Handle(subject); }, null);
                }
            }
        }

Connecting a message to the correct listeners is actually pretty simple.  The TestEventPublisher object as an ArrayList of all the possible listeners.  When it receives a message of type SystemLoaded, TestEventPublisher:

  • Loops through each listener
  • Checks if each listener implements the interface IListener<SystemLoaded>
  • If it does implement that interface, it calls Handle(SystemLoaded) on the listener

That’s it.  Assuming that the TestEventPublisher “knows” about all the listenters, and a client of ITestEventPublisher “knows” how to get to the single instance of TestEventPublisher, event aggregation is easy.  So let’s talk about getting the pieces together.

 

Getting Listeners and Publishers Together

The easy part is just getting a reference to the single instance of the ITestEventPublisher.  That’s just a little bit of vanilla StructureMap configuration:

            ForRequestedType<ITestEventPublisher>()
                .TheDefaultIsConcreteType<TestEventPublisher>().AsSingletons();

The bolded part just directs StructureMap to only create one shared instance of ITestEventPublisher.  Anything that needs to get at an ITestEventPublisher can just service location by a good ol’fashioned call to:

            ITestEventPublisher eventPublisher = 
                ObjectFactory.GetInstance<ITestEventPublisher>();

if you only need a transient reference to ITestEventPublisher.  The mass majority of the time I just use constructor injection like my ApplicationController class:

        public ApplicationController(
            ICommandExecutor executor, 
            IContentPanel panel, 
            IProjectRepository repository,
            ITestEventPublisher publisher, 
            IHierarchyNode hierarchyNode)
        {
            _executor = executor;
            _panel = panel;
            _repository = repository;
            _publisher = publisher;
            _hierarchyNode = hierarchyNode;
        }

I do create ApplicationController from StructureMap, so all of the constructor arguments are located and passed into the constructor by StructureMap.  All of the Command and Presenter classes that would need to broadcast event messages are generally created by StructureMap anyway, so the constructor injection method turns out to be pretty simple.

All that’s left is to make sure that the listeners are correctly registered with the TestEventPublisher, and that’s a little bit trickier.  Before I start, I should say that this is just the way that I did this and not THE way to do this.  In StoryTeller there are two different categories of listeners.  There is one set of objects like the FixtureExplorerPresenter that are driving pieces of the application shell at all times.  These objects are started as the application loads and registered with TestEventPublisher right off the bat.  I’m using the fluent interface mechanism for configuring StructureMap in code as a more or less internal DSL (I refuse to argue with you if it’s actually a DSL or not).  One of the advantages of an internal DSL written in just plain C# is that you can extend it at will or wrap it with other calls.  I created a little wrapper around the method to register a type with StructureMap called Start():

 

        private static List<Type> _startables = new List<Type>();
 
        protected CreatePluginFamilyExpression<T> Start<T>() where T : IStartable
        {
            Type type = typeof (T);
 
            // Just track which Types need to be started and registered with
            // ITestEventPublisher as the application is loaded
            _startables.Add(type);
 
            // Continue on with StructureMap configuration as usual
            return ForRequestedType<T>();
        }

In the StructureMap bootstrapping, I just call Start() to denote a type that needs to be started right off the bat and registered with the event aggregator.  Notice that our old friend FixtureExporerPresenter is one of the “Startables.”

            Start<IFixtureExplorerPresenter>()
                .TheDefaultIsConcreteType<FixtureExplorerPresenter>().AsSingletons();
 
            Start<IApplicationController>()
                .TheDefaultIsConcreteType<ApplicationController>().AsSingletons();
 
            Start<ICommandExecutor>().TheDefaultIsConcreteType<CommandExecutor>().AsSingletons();

 

Part of the application bootstrapping is a call to this little routine.  It just iterates through the “Startable” types, gets that type from StructureMap, and registers each by calling AddListener() on the event aggregator.

        private static void attachListeners()
        {
            ITestEventPublisher eventPublisher = 
                ObjectFactory.GetInstance<ITestEventPublisher>();
 
            foreach (Type type in _startables)
            {
                IStartable startable = (IStartable) ObjectFactory.GetInstance(type);
                startable.Start();
 
                eventPublisher.AddListener(startable);
            }
        }

I also register the Presenter for each screen as a listener each time a new screen is opened.  I’ll talk much more about this in a future post, but for right now let’s say that all screens are opened by a single class (sometimes it’s the ApplicationController, other times it’s what I call a ScreenCollection for a lack of a better name).  By making all screen activation and deactivation go through a common point it’s now easy to perform bookkeeping operations on the screens.  In the case of StoryTeller, I do this in the method that’s called to add a new screen to the main docking manager (the code is in ContentPanel if you’re playing along at home in the StoryTeller code):

 

        public void OpenScreen(IPresenter presenter)
        {
            _publisher.AddListener(presenter);
 
            ...Other stuff that adds the screen to the docking manager
        }

I mentioned above that I don’t use WeakReferences.  To avoid a severe memory leak in the app, I need to remove a presenter from the publisher’s list of listeners whenever a screen is permanently closed.

 

        public void Close(ContentTab tab)
        {
            bool needToActivateDifferentScreen = tab.Equals(TabControl.SelectedTab);
 
            IPresenter presenter = tab.Presenter;
            presenter.Deactivate();
            _publisher.RemoveListener(presenter);
 
            _tabControl.Items.Remove(tab);
 
            if (needToActivateDifferentScreen && _tabControl.Items.Count > 0)
            {
                ContentTab lastTab = getLastTab();
                TabControl.SelectedTab = lastTab;
            }
        }

If you’re not going to track the elements that need to be removed from the publish/subscribe registry, or just don’t want to do this, I’d recommend using WeakReferences inside your Event Aggregator class.

Other Issues

Some last little thoughts. 

  • I’m using SynchronizationContext for threading synchronization so that I don’t have to worry about events coming from a background thread.  I’ve found that choking the background callbacks through just a couple different points can keep the rest of your code cleaner from the monotonous InvokeRequired()/Invoke() code. 
  • I don’t think it matters in StoryTeller, but with other applications you may not want to immediately respond to an event if the screen is hidden or in an inactive tab.  You’ll need more logic in these cases to delay an update activity.

 There’s probably other issues, but I want to hit “publish” now.  Just throw in questions and I’ll see what I can do.

About Jeremy Miller

Jeremy is the Chief Software Architect at Dovetail Software, the coolest ISV in Austin. Jeremy began his IT career writing "Shadow IT" applications to automate his engineering documentation, then wandered into software development because it looked like more fun. Jeremy is the author of the open source StructureMap tool for Dependency Injection with .Net, StoryTeller for supercharged acceptance testing in .Net, and one of the principal developers behind FubuMVC. Jeremy's thoughts on all things software can be found at The Shade Tree Developer at http://codebetter.com/jeremymiller.
This entry was posted in Build your own CAB, Featured. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • Garusher

    great stuff, i now use this model alot when i create collections of controls, instead of using eventhandlers.

  • jb

    i would like to study this article but the code is cut off on the right side

  • saleh al-abbas

    I was amazed not to see any comment. However, I noticed that I myself skimmed the post when I reached the illustration of the concept using the StoryTeller example. So, maybe nobody finished / thoroughly read the post bcoz of the example used.
    Actually, I encountered the same challenges you mentioned. So, I read the post for enlightenment. I will read the post again bcoz the example made it hard to understand.

  • http://kentb.blogspot.com Kent Boogaart

    Hey Jeremy,

    Apologies again for the late reply – I’m slowly catching up on your excellent series on my bus trips to and from work. After reading this post I felt like putting together my own version of how I feel event aggregation could work. Basically, I made the event aggregator interface strongly typed (including the subscribe methods) and moved reflection usage into the auto-wiring code, where I feel it belongs. Also, publishes don’t need to implement an interface – they just use the event aggregator service.

    Perhaps we should IM on this or something because I’m really keen to get your thoughts and the code is probably too long to post here. Basically, the usage looks like this:

    public class MyClass : ISubscriber
    {
    public void Receive(SomethingHappened subject)
    {
    //something happened
    }
    }

    public class SomeOtherClass
    {
    private IEventAggregator _eventAggregator = …;

    public void SomeMethod()
    {
    _eventAggregator.Publish(new SomethingHappened());
    }
    }

    Anyways, let me know.

    Regards,
    Kent

  • http://codebetter.com/blogs/jeremy.miller Jeremy D. Miller

    @Reshef,

    That’s *a* way to do that. In a very similar scenario, I had the Command that was fired off of the TreeNode just tell the ApplicationController to launch a new instance of the screen Presenter.

  • Reshef Mann

    Is using the EventPublisher the way to communicate between presenters?
    Let me be clearer, say I have a file editor with a text editor component and a file browser tree which holds the tree of scripts I have. I have a presenter for each of there components. Should the message that tells the text editor presenter that the user wants to open a file (doubleclicks it in the tree) arrive by the event publisher?
    Thanks, Reshef

  • http://www.bluespire.com/blogs Rob

    Thought you might find this interesting:

    http://mdavey.wordpress.com/2007/06/14/ui-automation/

  • http://codebetter.com/blogs/jeremy.miller Jeremy D. Miller

    @Nathan,

    I’m not sure that you could get away with injecting the listeners via IoC, or that you would want to. Most of the listeners in StoryTeller wouldn’t even get created until well after the publisher. It wouldn’t be quite as dynamic.

    I guess my real answer is “don’t know”

  • Nathan

    This looks simple but clever. How would you compare this to injecting a list of listeners into your publisher via IoC ?

  • http://codebetter.com/blogs/jeremy.miller Jeremy D. Miller

    Rob,

    I didn’t do a very good job of explaining that the “T” in IListener is the subject of the message. You would just make a type called:

    public class ThisJustHappened{}

    and go PublishEvent(new ThisJustHappened())

    to tell an IListener () to call it’s Handle () method. In this case, all the message type is good for is routing

  • http://www.bluespire.com/blogs Rob

    I think this is a really elegant way to solve the problem. I’m not sure why it didn’t occur to me to do it like this before. It must have been obvious to Udi, as this is a very common pattern in his area. There is one thing I’m not sure of. How would you handle the case of a “dataless” event?