Firing generic events with EventAggregator

It’s been a while since i posted anything on Prism. When I left p&p, i said you would see more posts on Prism on my blog. I’ve been pretty immersed in MEF since leaving and haven’t done any posts on Prism yet. Today, I got inspired (albeit late in the evening) based on a post on the forums to actually do that, so here I am :-). The post was from TSChaena about using EventAggregator to fire generic events similar to the way we did things with EventBroker in CAB. Using EventBroker allows you to dynamically define events in your application that are identified through a topic name rather than needing to define a strongly typed class as you do with the EventAggregator. There are several advantages to not using the approach in EB which I have identified in this post. However, there are times when you want to do a dynamic eventing system. The good news is that there actually is a solution for doing this with our EventAggreator though it is not exactly the same as the way we did it in EB.


CompositeWPFEvent


Before we look at the solution I came up with, lets talk quickly about CompositeWPFEvent. CompositeWPFEvent is a generic class that contains one type parameter TPayload. TPayload defines the payload that will be passed into the event when it is fired. The subscriber also uses the payload to provide filters for the subscription. For example if TPayload is a FundOrder as in the EventAggregator QuickStart, then you can supply a lambda such as fundOrder=>fundOrder.CustomeriD == customerID for example to filter on only events that are received for a specific customer. The common pattern you will see for defining such events is to create a class that inherits from CompositeWPFEvent for each event that is typed to the specific paramers. For example below is the definition for the FundOrderAdded event.


public class FundOrderAdded : CompositeWpfEvent<FundOrderAdded> {}

This event is then retrieved from the EventAggregator by calling the GetEvent method passing FundOrderAdded as the event. Now, although this is the common pattern, there is nothing about the EventAggregator that requires you to create a new event class for each event. CompositeWPFEvent is not an abstract class, so you can simply “use” it as you will, even in a generic case. For example you can do the following.



   1: public class ThrowsEvents {
   2:   public ThrowsEvents(IEventAggregator eventAgg) {
   3:     eventAgg.GetEvent<CompositeWPFEvent<string>>().Publish(“SomethingEvent”)
   4:     eventAgg.GetEvent<CompositeWPFEvent<string>>().Publish(“SomethingElseEvent”)
   5:   }
   6: }
   7:  
   8: public class HandlesEvents {
   9:   public HandlesEvents(IEventAggregator eventAgg) {
  10:     CompositeWPFEvent genericEvent = eventAgg.GetEvent<CompositeWPFEvent<string>>();
  11:     genericEvent.Subscribe(action=>Console.WriteLine(“SomethingEvent fired”, ThreadOption.UIThread, 
  12:       false, e=>e == “SomethingEvent”);
  13:     genericEvent.Subscribe(action=>Console.WriteLine(“SomethingElseEvent fired”, ThreadOption.UIThread, 
  14:       false, e=>e == “SomethingElseEvent”);
  15:   } 
  16: }

If you look at the above code, you’ll notice that we are using CompositeWPFEvent event directly, rather than creating a specific inheritor. When we call for the event from the aggregator, we are passing in a param of type string which represents the EventName / Topic. I am then using our event subscription mechanism to subscribe two different handlers to the same “event” by using the eventName as the filter. So here we have the basics of doing generic event publication and subscription. However, we are missing something important….that payload :). To handle this, you could instead create your own custom class that carries two parameters, EventName, and Value. With that approach, you can pass both the event name and the value, still filter on the event, and you can pass a value along. For example the above code passing  a value would look like the following.



   1: public class SomeEventParams {
   2:   public SomeEventParams(string eventName, object value) {
   3:     EventName = eventName;
   4:     Value = value;
   5:   }
   6:   
   7:   public string EventName {get;private set;}
   8:   public object Value {get; private set;}
   9: }
  10:  
  11:  
  12: public class ThrowsEvents {
  13:  
  14:   public ThrowsEvents(IEventAggregator eventAgg) {
  15:     eventAgg.GetEvent<CompositeWPFEvent<SomeEventParams>>().Publish(new SomeEventParams(“SomethingEvent”,“SomeValue”));
  16:     eventAgg.GetEvent<CompositeWPFEvent<SomeEventParams>>().Publish(new SomeEventParams(“SomethingElseEvent”, “SomeOtherValue”));
  17:   }
  18: }
  19:  
  20: public class HandlesEvents {
  21:   public HandlesEvents(IEventAggregator eventAgg) {
  22:     CompositeWPFEvent genericEvent = eventAgg.GetEvent<CompositeWPFEvent<string>>();
  23:     genericEvent.Subscribe(action=>Console.WriteLine(“SomethingEvent fired” + action.Value, ThreadOption.UIThread, 
  24:       false, e=>e.EventName == “SomethingEvent”);
  25:     genericEvent.Subscribe(action=>Console.WriteLine(“SomethingElseEvent fired” + action.Value, ThreadOption.UIThread, 
  26:       false, e=>e.EventName == “SomethingElseEvent”);
  27:   } 
  28: }

That’s OK, except now the parameters are simply object. That means we are losing the type safety that the EventAgg was built for in the first place! Now you can further refactor and make SomeEventParams a generic type that accepts a type param for the value. The only downside of this, is the code will get much more verbose and harder to read. For example retrieving the event to publish will now look like…


eventAgg.GetEvent<CompositeWPFEvent<SomeEventParams<string>>().Publish…

Suboptimal. I bet your thinking you could refactor this a bit more..yes, you can. This is what led me to a GenericEvent.




GenericEvent


If we keep refactoring, we can get rid of alot of the DRY behavior, by creating an inheritor of CompositeWPFEvent, GenericEvent. The event and associated parameters class looks like this



public class EventParameters<TValue>
{
  public string Topic { get; private set; }
  public TValue Value { get; private set; }
 
 
  public EventParameters(string topic, TValue value)
  {
    Topic = topic;
    Value = value;
  }
}
 
public class GenericEvent<TValue> : CompositeWpfEvent<EventParameters<TValue>> {}

Subscribing and publishing is now easier as well. The previous GetEvent code now looks like


eventAgg.GetEvent<GenericEvent<string>().Publish…

Because I have strongly typed my Value, I know have back my strongly typed filters and delegates.


Putting the rubber to the road with the EventAggregation QuickStart.


In order to test this out, I took a copy of the EventAggregaton QuickStart that is included with the Prism bits, and I modified it to use the new GenericEvent. I also added a Remove button to the QS in order to demonstrate using more than one event. The new Quickstart looks like the following.


image 


In the new version of the Quickstart, the FundOrderAddedEvent is removed. Instead, I have added two constants to define the different events.



public class Events
{
  public const string FundAdded = “FundAdded”;
  public const string FundRemoved = “FundRemoved”;
}

I added a RemoveFund method to the AddFundPresenter as well as refactored the AddFund method as follows.



void RemoveFund(object sender, EventArgs e)
{
    FundOrder fundOrder = new FundOrder();
    fundOrder.CustomerId = View.Customer;
    fundOrder.TickerSymbol = View.Fund;
 
    if (!string.IsNullOrEmpty(fundOrder.CustomerId) && !string.IsNullOrEmpty(fundOrder.TickerSymbol))
        eventAggregator.GetEvent<GenericEvent<FundOrder>>().
          Publish(new EventParameters<FundOrder>(Events.FundRemoved, fundOrder));
    
}
 
void AddFund(object sender, EventArgs e)
{
    FundOrder fundOrder = new FundOrder();
    fundOrder.CustomerId = View.Customer;
    fundOrder.TickerSymbol = View.Fund;
 
    if (!string.IsNullOrEmpty(fundOrder.CustomerId) && !string.IsNullOrEmpty(fundOrder.TickerSymbol))
        eventAggregator.GetEvent<GenericEvent<FundOrder>>().
          Publish(new EventParameters<FundOrder>(Events.FundAdded, fundOrder));
}

Finally, I refactored the ActivityPresenter in a similar fashion



public string CustomerId
{
    get { return _customerId; }
    set
    {
        _customerId = value;
 
        GenericEvent<FundOrder> fundOrderEvent = eventAggregator.GetEvent<GenericEvent<FundOrder>>();
 
        if (fundAddedSubscriptionToken != null)
        {
            fundOrderEvent.Unsubscribe(fundAddedSubscriptionToken);
            fundOrderEvent.Unsubscribe(fundRemovedSubscriptionToken);
            
        }
 
        fundAddedSubscriptionToken = fundOrderEvent.Subscribe(FundAddedEventHandler, ThreadOption.UIThread, false,
                                                     parms => parms.Topic == Events.FundAdded && parms.Value.CustomerId == _customerId);
 
        fundRemovedSubscriptionToken = fundOrderEvent.Subscribe(FundRemovedEventHandler, ThreadOption.UIThread, false,
                                                     parms => parms.Topic == Events.FundRemoved && parms.Value.CustomerId == _customerId);
 
        View.Title = string.Format(CultureInfo.CurrentCulture, Resources.ActivityTitle, CustomerId);
    }
}

Notice how in the subscription I am now filteirng on the event Topic in addition to the value. This is the result of moving to a generic event.



Wrapping Up


Using the approach show in this post, we’ve seen how you can utilize the existing EventAggregator infrastructure to do generic eventing similar to the way EventBroker in CAB functions.


Personally I think using strongly typed specific events is more maintainable. The reasoning is because the event payload type is intrinsically defined to the event wheras in this model they are not. For example with generic events I might have an event that publishes passing a customer, but on the receiving side I have  defined it as a string. This event will never get handled, because the susbscriber and publisher don’t match. If you use strongly typed events that is not the case, as the type is the match ;) However there are scenarios where it may make sense to have something more dynamic, for example if you have a metadata driven system that needs to do dynamic wiring.


In the link below, you’ll find the code for my modified version of the QuickStart. Let me know if this works for you. Now time to get some sleep :)


http://tinyurl.com/GenericEventAgg-zip

This entry was posted in Composite Application Guidance, Composite WPF, prism. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • http://www.facebook.com/hchattaway Harold Chattaway

    Hello Glenn,

    I am trying to do pretty much exactly what you have described here. I have an event that uses an eventarg class that has a generic parameter. I want to publish this event and have the subscriber act on the generic parameter that was passed.

    on the Publish side I have this PublishPrismEvent.Publish(new PrismPublishEventArgs(this.MethodName, this.TargetName,  argument));
    “argument” is defined as a generic parameter in the PrismPublishEventArgs class. On the subscribe side, I wish to subscribe to this event and then analyze that generic parameter using reflection, for example, in the event handler. I have:            PrismPublishEvent PrismInternalPublishEvent = EventAggregator.GetEvent();
                PrismInternalPublishEvent.Subscribe(PrismInternalPublishEventHandler, ThreadOption.UIThread, false);
    but it is throwing an error saying “Incorrect number of type parameters in reference to class PrismPublishEvent”This should be doable… any help would be greatly appreciated. The link to the sample project is no longer valid.

    Thanks
    Harold

  • http://codebetter.com/members/vgupta/default.aspx vgupta

    Hi Glenn,
    I had a implementation for threading Using EventAggregator. Can you please review it at http://stackoverflow.com/questions/2190178/can-prism-eventaggregator-be-used-for-threading-needs
    Please guide me if this could be a possible implementation using EventAggregator

    Thanks

  • Chakshu Shah

    I still am not able to download the code attached with this link. It says ‘Page not found’.

  • Berch

    Nope, Thank you !!

    =-}

  • http://www.codebetter.com/blogs/glenn.block/ Glenn Block

    Hi Berch

    You are right, it is corrupted :) Seems like it may have been that way for a long time. I tried updating another zip and the same thing happened.

    Anyway, luckily I still have the original. I went and uploaded it to a skydrive. You can get it at the link below (which I also added to my post).

    http://tinyurl.com/GenericEventAgg-zip

    Thanks

  • Berch

    Hi Glenn

    It seems like the attached zip is corrupt…. :-(
    And it looks just like what i need. Can you pleeeeeezzzz check it ? maybe I’m doing something wrong ??
    many thx

    Hai
    haib@innovashare.com

  • http://www.codebetter.com/blogs/glenn.block/ Glenn Block

    @Udi, the event implementation we shipped with Prism actually does bind the two. CompositeWpfEvent is bound to the parameters.

    This implementation (which needs a bunch of cleaning) is for dynamically creating an event from a string.

  • http://www.UdiDahan.com Udi Dahan: The Software Simplist

    Fixing the link in the previous comment:

    http://www.udidahan.com/2008/08/25/domain-events-take-2/

  • http://www.UdiDahan.com Udi Dahan: The Software Simplist

    I’ve got to say that the fact that the definition of the event (const string) is separate from the parameter definition is… unfortunate.

    I think it would be simpler to keep the definition of events/commands in one place like this:

    public static class DomainEvents
    {
    public static readonly DomainEvent ProductReportedLost = new DomainEvent;

    public static readonly DomainEvent CartIsFull = new DomainEvent;
    }

    This holds for composite client commands and server-side domain model events.

    I’ve got the infrastructure that supports this up on my post Domain Events – Take 2.

    Thoughts?

  • http://weblogs.asp.net/sfeldman Sean Feldman

    Is there a road map for Prism project? Would be interestng to know where are you heading with it. Thank you.