title: Event aggregator for asp.net
body: <p><strong>My needs</strong></p>
  
  <p>I would like our in house, standard product to fire different events when things happen.
  In global asax on different custom solutions, I would like to hookup on these events, when needed, and react.</p>
  
  <p><strong>Existing modules</strong></p>
  
  <p>I have been looking for an event aggregator for asp.net but I'm not really sure what to use.
  I have read about Prism, but it seems, that this targets WPF/Silverlight and not asp.net.</p>
  
  <p>Then there is this guy, who seems to have ported the aggregator into his own version, independent of WPF: <a href="http://weblogs.asp.net/rashid/archive/2009/03/05/use-event-aggregator-to-make-your-application-more-extensible.aspx" rel="noreferrer">http://weblogs.asp.net/rashid/archive/2009/03/05/use-event-aggregator-to-make-your-application-more-extensible.aspx</a></p>
  
  <p><strong>Question</strong></p>
  
  <p>Have anyone experiences using an event aggregator for asp.net? This is for production use, so I prefer not to use some home-coded aggregator from a random guy on the net :)</p>
  
  <p>Thankyou in advance.</p>
  
  <p><strong>EDIT 1:</strong>
  It seemed, that NServiceBus was a little overkill for the purpose. I created a single EventAggregator class that does the trick.</p>
  
  <p>The class:</p>
  
  <pre><code>/// &lt;summary&gt;
  /// A event aggregator.
  /// &lt;/summary&gt;
  public class EventAggregator
  {
  /// &lt;summary&gt;The object to use when locking.&lt;/summary&gt;
  private readonly object _lock = new object();
  /// &lt;summary&gt;Holder of registered event handlers&lt;/summary&gt;
  private readonly Dictionary&lt;Type, List&lt;object&gt;&gt; _handlers = new Dictionary&lt;Type, List&lt;object&gt;&gt;();
  /// &lt;summary&gt;Registers the specified handler.&lt;/summary&gt;
  /// &lt;typeparam name="T"&gt;&lt;/typeparam&gt;
  /// &lt;param name="handler"&gt;The handler.&lt;/param&gt;
  public void Register&lt;T&gt;(EventHandler&lt;T&gt; handler) where T : EventArgs
  {
      lock (_lock)
      {
          if (!_handlers.ContainsKey(typeof (T)))
              _handlers.Add(typeof (T), new List&lt;object&gt;());
          _handlers[typeof (T)].Add(handler);
      }
  }
  /// &lt;summary&gt;Publishes the specified event.&lt;/summary&gt;
  /// &lt;typeparam name="T"&gt;&lt;/typeparam&gt;
  /// &lt;param name="sender"&gt;The sender.&lt;/param&gt;
  /// &lt;param name="eventToPublish"&gt;The event to publish.&lt;/param&gt;
  public void Publish&lt;T&gt;(object sender, T eventToPublish) where T : EventArgs
  {
      lock (_lock)
      {
          if (!_handlers.ContainsKey(typeof (T)))
              return; // No listers for event
          foreach (EventHandler&lt;T&gt; handler in _handlers[typeof (T)])
              handler.Invoke(sender, eventToPublish);
      }
  }
  }
  </code></pre>
  
  <p>An event class:</p>
  
  <pre><code>public class EntityDeleted : EventArgs
  {
  }
  </code></pre>
  
  <p>Registering an event handler in global asax:</p>
  
  <pre><code>aggregator.Register&lt;EntityDeleted&gt;((s, e) =&gt; {
  // Do stuff here
  });
  </code></pre>
  
  <p>Raising an event:</p>
  
  <pre><code>aggregator.Publish(this, new EntityDeleted());
  </code></pre>
  
  <p><strong>EDIT 2:</strong></p>
  
  <p>And here are my unit test for the ones interrested:</p>
  
  <pre><code>/// &lt;summary&gt;
  /// Unit tests for EventAggregator
  /// &lt;/summary&gt;
  [TestClass]
  public class EventAggregatorTest
  {
      /// &lt;summary&gt;Tests that no exceptions are thrown when calling an event with no handlers.&lt;/summary&gt;
  [TestMethod]
  public void EmptyAggregatorTest()
  {
      var aggregator = new EventAggregator();
      aggregator.Publish(this, new TestEventOne() { Property = "p1" });
  }
  /// &lt;summary&gt;Tests the aggregator using a single, registered handler.&lt;/summary&gt;
  [TestMethod]
  public void SingleListenerTest()
  {
      var aggregator = new EventAggregator();
      int calls = 0;
      aggregator.Register&lt;TestEventOne&gt;((sender, e) =&gt;
      {
          Assert.AreEqual("p1", e.Property);
          calls ++;
      });
      Assert.AreEqual(0, calls);
      aggregator.Publish(this, new TestEventOne(){Property = "p1"});
      Assert.AreEqual(1, calls);
  }
  
  /// &lt;summary&gt;Tests the aggregator using multiple registered handlers.&lt;/summary&gt;
  [TestMethod]
  public void MultipleListenersTest()
  {
      var aggregator = new EventAggregator();
      int p1Calls = 0;
      int p2Calls = 0;
      aggregator.Register&lt;TestEventOne&gt;((sender, e) =&gt;
      {
          Assert.AreEqual("p1", e.Property);
          p1Calls++;
      });
      aggregator.Register&lt;TestEventOne&gt;((sender, e) =&gt;
      {
          Assert.AreEqual("p1", e.Property);
          p1Calls++;
      });
      aggregator.Register&lt;TestEventTwo&gt;((sender, e) =&gt;
      {
          Assert.AreEqual("p2", e.Property);
          p2Calls++;
      });
      Assert.AreEqual(0, p1Calls);
      aggregator.Publish(this, new TestEventOne() { Property = "p1" });
      Assert.AreEqual(2, p1Calls);
      Assert.AreEqual(0, p2Calls);
      aggregator.Publish(this, new TestEventTwo() { Property = "p2" });
      Assert.AreEqual(1, p2Calls);
      Assert.AreEqual(2, p1Calls);
  }
  }
  
  /// &lt;summary&gt;
  /// Dummy test event 1
  /// &lt;/summary&gt;
  public class TestEventOne : EventArgs
  {
      public string Property { get; set; }
  }
  /// &lt;summary&gt;
  /// Dummy test event 2
  /// &lt;/summary&gt;
  public class TestEventTwo : EventArgs
  {
      public string Property { get; set; }
  }
  </code></pre>
  
  <p><strong>EDIT 3:</strong></p>
  
  <p>Thanks to Steven Robbins for pointing out, that the aggregator was not thread safe, I added locking to the Publish and Register methods.</p>