Sunday 18 November 2012

Tracking Prism EventAggregator traffic

Tracking Prism Events

It is possible to track the Prism events that are sent and received through the Prism EventAggregator,
but this is not out of the box of the Prism Framework. Usually, the developer must insert breakpoints
where the CompositePresentationEvent is published and subscribed

This is satisfactory for small scenarios, but as your solution and project(s) grow, it can become an increasingly difficult task. It is in many cases desired to see which events are published and where they are subscribed. The Prism 4.x Framework still do not support this, but luckily there is a way to achieve this. 

First off, it is required to change your Prism Events from using the class CompositePresentationEvent, to instead use a derived class presented next, CustomCompositePresentationEvent. I have chosen to display the Prism Event traffic in the Output of Visual Studio. This is done using Debug.WriteLine. It could be perhaps an alternative to log this event traffic or display it in a separate Window if you are writing a WPF application. 

The custom class CustomCompositePresentationEvent looks like this: 
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Text;
using Microsoft.Practices.Prism.Events;

namespace EventAggregation.Infrastructure
{
    
    /// <summary>
    /// Custom CompositePresentationEvent class, derived from this base class to allow the Prism Events to be displayed in the Debug Output Window. 
    /// </summary>
    public class CustomCompositePresentationEvent<TEvent> : 
    CompositePresentationEvent<TEvent> where TEvent : class 
    {

        /// <summary>
        /// Overload of the publish method of CompositePresentationEvent 
        /// </summary>
        public override void Publish(TEvent payload)
        {
            var declaringType = string.Empty; 
            var callingMethod = new StackTrace().GetFrame(1).GetMethod(); 
            if (callingMethod != null)
                declaringType = callingMethod.DeclaringType.FullName; 
            System.Diagnostics.Debug.WriteLine(string.Format(@"Publishing event {0}
            at time: {1}. Source type: {2}. Event Payload: {3}", 
            typeof(TEvent).Name,DateTime.Now, declaringType, 
            GetContentAsString(payload))); 
            base.Publish(payload);
        }

        /// <summary>Overload of the Subscribe method of  CompositePresentationEvent</summary>
        /// <remarks>
        /// For this overload to be executed, set keepSubscriberReferenceAlive to 
        /// true in the subscribe setup method on the target. 
        /// </remarks>
        public override SubscriptionToken Subscribe(Action<TEvent> action,
        ThreadOption threadOption, bool keepSubscriberReferenceAlive, 
        Predicate<TEvent> filter)
        {

            var loggingAction = new Action<TEvent>(e =>
            {
                System.Diagnostics.Debug.WriteLine(string.Format(
                "Subscribing event {0} at time: {1}. Target type: {2}", 
                typeof(TEvent).Name,
                    DateTime.Now, action.Target.GetType().Name));

                action.Invoke(e);
            });

            var subscriptionToken = base.Subscribe(loggingAction, threadOption, 
            keepSubscriberReferenceAlive, filter);
            return subscriptionToken;
        }


        private string GetContentAsString(TEvent payload)
        {
            if (payload == null)
                return string.Empty;
            else
            {
                StringBuilder sb = new StringBuilder();
                foreach (PropertyDescriptor property in 
                 TypeDescriptor.GetProperties(payload))
                {                    
                    sb.Append(string.Format("{0}={1} ", property.Name, 
                    property.GetValue(payload))); 
                }
                return sb.ToString();                 
            }
        }

    }

}

As you can see from the source code above, the methods Subscribe and Publish are overriden. These are virtual methods of the CompositePresentationEvent base class. I have tested using this derived class from CompositePresentationEvent in the EventAggregator sample, which can be downloaded in the Prism 4.1 library with source code at the following url: Download Prism 4.1 You will find the EventAggregator sample in the \QuickStarts\EventAggregation folder after you have extracted the Prism 4.1 library with source code. I have done some minor changes to this demo solution. To the project EventAggregation.Infrastructure.Desktop, I added the class I made which was just presented, CustomCompositePresentationEvent. Next up, our Prism Events must use this CustomCompositePresentation event class! This is easy to change, as shown in the Prism Event FundAddedEvent, which I changed to the following, still in the project EventAggregation.Infrastructure.Desktop:

namespace EventAggregation.Infrastructure
{
    public class FundAddedEvent : CustomCompositePresentationEvent
    {
    }
}

As you can see, the FundAddedEvent Prism Event, is now inheriting from the class CustomCompositePresentationEvent. There are no changes needed to do for the way the FundAddedEvent is published, but the Subscribe method must be changed. The boolean flag keepSubscriberReferenceAlive must be set to true. The reason is that the Subscribe override method in the CustomCompositePresentationEvent requires this, as you can see in the source code. We modify the action sent back again to the base class to continue with its Subscribe implementation. I could not make the override method to work, i.e. the method override was not called, without setting the boolean flag keepSubscribeReferenceAlive to true. The change I had to make for the subscribe method of the FundAddedEvent is in the project ModuleB.Desktop, in the class ActivityPresenter. The line 67 was changed to this:

  subscriptionToken = fundAddedEvent.Subscribe(FundAddedEventHandler,  
  ThreadOption.UIThread, true, FundOrderFilter); 
The changes required then, is to change the Prism Events to inherit from the class CompositePresentationEvent to instead inherit from (use) the class CustomCompositePresentationEvent. This custom class is basically the CompositePresentationEvent class, but we have overridden the methods Subscribe and Publish. In addition, we must set the keepSubscriberReferenceAlive boolean flag to true in our Subscribe methods. This will potentially mean that you must change to the Subscribe overload method which has this flag. Let's first see the GUI of the EventAggregation sample:







The output from the Visual Studio is the most important feature of this CustomCompositePresentationEvent class. After all, we want to be able to see which classes publish and subscribe events, and the content of the payload of these events, correct? In this sample, the output looks like the following example:

Publishing event FundOrder at time: 18.11.2012 15:31:30. Source type: ModuleA.AddFundPresenter. Event Payload: CustomerId=Customer1 TickerSymbol=FundB 
Subscribing event FundOrder at time: 18.11.2012 15:31:30. Target type: ActivityPresenter

As we can see from the output above, the event FundOrder was published by the AddFundPresenter class. The PayLoad of this Prism event was CustomerId=Customer1 and the TickerSymbol=FundB. The event FundOrder was subscribed, interestingly at the same millisecond (i.e. the publish-subscribe execution obviously performs almost instantly in the Prism framework). If you think the concept of being able to see Prism traffic is both interesting and will help you as a developer, consider taking into to use this CompositePresentationEvent class. You will have to include the changes which I listed above also, but these changes should be relatively easy to overcome in a Prism application solution. The tracking or tracing of Prism Event Traffic will help you understand the application better and catch possible errors by this more convenient debugging scenario. For security reasons, you should consider if putting a Debug.WriteLine call to output the Event Payload is safe or not. Last, I want to mention the fact that Prism Publish-Subscribe scenarios take in my test 0 ms and occur to be happening almost instantly. Of course they take some CPU cycles to execute, but if you in any case wonder how fast is really a Publish-Subscribe, it will usually be the answer - REALLY FAST. However, it is possible that if multiple classes subscribe to a given Prism Event, it might take some milliseconds to execute. I have not tested this yet. In a MVVM Scenario, it is usually the ViewModels that publish and subscribe Prism Events, usually via an ICommand implementation (DelegateCommand) that is data bound in WPF or Silverlight to a Button or similar GUI object.
Share this article on LinkedIn.

2 comments:

  1. I will suppose this feature is a welcome capability to multiple WPF and Silverlight solutions. Finally, monitoring the publishing and subscriptions of Prism Events are not black arts again.

    ReplyDelete
  2. This comment has been removed by the author.

    ReplyDelete