When you make some code based on events it's easy to lose control on what events are registered where. I therefore made my self a small report to inspect the different page events and to show me the registered events for each of them. The different events are registered in EPiServer.DataFactory.Instance object. Events are a form of Delegate so if you can access the underlying field of a event you can cast it to MulticastDelegate and get the different registered delegates as shown in Stephen's blog post.
First a preview of what this code does.
It list all the Page events, so usages and who many events are registered. It also show more info about each event registered. This code can easy be changed to make some events always be the last event that is fired. Or to exclude some events by removing them, or make a wrapper around them and only execute them when it appropriated.
The code is pretty straight forward, but as usually when you want to do fun things you have to access private fields. The page events from DataFactory are stored in a private field _events which is of type EventHandlerList.
public event PageEventHandler CheckedInPage
{
add
{
this._events.AddHandler(this.CheckedInEvent, value);
}
remove
{
this._events.RemoveHandler(this.CheckedInEvent, value);
}
}
This class has use a object as an key to get the underlying delegate to the event. I therefore have to get the private _event field, and the private object that are keys too the EventHandlerList
Make myself a function to access private fields called GetPrivateField.
Since am lacy I used Reflection the get all the fields inside the DataFactory object and made myself a Dictionary with key,object. Always when I'm making more then 4-5 variables that have the same function I always make some logic so I don't need to create all those variables. That is apparently not how EPiServer does it :).
static Dictionary<string, object> eventKeys;
public static Dictionary<string, object> EventKeys
{
get
{
if (eventKeys == null)
{
Dictionary<string, object> tmp = new Dictionary<string, object>();
Type t = EPiServer.DataFactory.Instance.GetType();
FieldInfo[] fields = t.GetFields(BindingFlags.Instance | BindingFlags.NonPublic);
foreach (FieldInfo field in fields)
{
if (!field.Name.StartsWith("_"))
{
object keyObject = EventReport.GetPrivateField(field.Name, EPiServer.DataFactory.Instance);
if (!tmp.ContainsValue(keyObject))
tmp.Add(field.Name, keyObject);
}
}
eventKeys = tmp;
}
return eventKeys;
}
}
With the key,object dictionary and the Event(EventHandlerList) I could use private function Find on EventHandlerList with the object as a key. I could then get the underlying Delegate, and cast it too Multcastelegate.
public System.Delegate[] GetEvents(string key)
{
if (!EventKeys.ContainsKey(key))
return null;
object keyObject = EventKeys[key];
Type type = Events.GetType();
MethodInfo method = type.GetMethod("Find", BindingFlags.Instance | BindingFlags.NonPublic);
List<object> args = new List<object>();
args.Add(keyObject);
object eventStore = method.Invoke(Events, args.ToArray());
if (eventStore == null) return null;
object handler = GetPrivateField("handler", eventStore);
System.MulticastDelegate originalDelegate = (System.MulticastDelegate)handler;
System.Delegate[] result=originalDelegate.GetInvocationList();
return result;
}
After I hade done this, I added my own events to count usages of the different events. I could have done this with one line of code for each event, but it felt better to use the keys I had already assembled. The ChildrenEvent had an other EventArgs class so I had to exclude them (still lacy).
public class EventReportStartUp : EPiServer.PlugIn.PlugInAttribute
{
public static Dictionary<string, EventReportCount> CountList = new Dictionary<string, EventReportCount>();
public static void Start()
{
Type t = EPiServer.DataFactory.Instance.GetType();
FieldInfo[] fields = t.GetFields(BindingFlags.Instance | BindingFlags.NonPublic);
MethodInfo method = EventReport.Events.GetType().GetMethod("AddHandler", BindingFlags.Instance | BindingFlags.NonPublic);
foreach (string key in EventReport.EventKeys.Keys)
{
if (!key.Contains("Children"))
{
EventReportCount item = new EventReportCount();
PageEventHandler handler = new PageEventHandler(item.Count);
EventReport.Events.AddHandler(EventReport.EventKeys[key], handler);
CountList.Add(key, item);
}
}
}
}
public class EventReportCount
{
int count = 0;
object lockObject = new object();
public int GetCount
{
get { return count; }
}
public void Count(object sender, EventArgs e)
{
lock (lockObject)
{
count++;
}
}
}
I then wrapped it up as an report as Mats describes in his article.
Download