Often we find it easy to create a class with a static event to keep the controls in our web application loosely coupled. The easiest way to make a number of controls interact without “knowing” about each other is to have a static event distributor class.
public class EventDistributor
public static event EventHandler SomethingHappened;
public static void RaiseSomethingHappened(object sender, EventArgs e)
if (SomethingHappened != null)
Some of the controls raise the events of the distributor…
protected void Button1_Click(object sender, EventArgs e)
…and others subscribe to them
protected void Page_Load(object sender, EventArgs e)
EventDistributor.SomethingHappened += new EventHandler(EventDistributor_SomethingHappened);
Let’s say we add an IScriptControl (e.g. from the RadControls for ASP.NET AJAX
suite) in the control tree inside the event handler of the static event. After postback we get an InvalidOperationException
with the message “Script controls may not be registered after PreRender”.
To find the reason for the error we need to know that static classes and members are alive until the application is restarted. They are not destroyed after the page lifecycle completes. So what happens? A control subscribes to the event of the event distributor in the Page_Load handler. After the postback the event is raised and the control’s event handler method from the first request gets executed. The life cycle of that previous page was already executed and adding a RadControl to the Controls tree will throw the aforementioned exception. This exception is thrown by the ScriptManager control which cannot register script controls after the Prerender event of the Page object.
There is one more problem here – the event distributor holds a reference to the user control and thus it cannot be garbage collected. This leads to a memory leak on the server.
What we need to do is guarantee that the event distributor does not keep a reference to our object. This can be done easily by detaching the event handler in a later stage of the page lifecycle. Still, this is not the best approach. As the static members are alive as long as the application is, it is possible that concurrent requests to the same page execute simultaneously. This will raise the event twice for both page instances and will execute their event handler methods twice. We can avoid that by making sure our class is page-specific. We can create a singleton static class, which is stored in the Items collection of the Page object and use it. The singleton will be destroyed when the page gets destroyed, so we even do not need the code to remove the event handler.
I’m attaching a demo project
to this post. It contains two folders – one of them contains a page, demonstrating the problem and the other contains the suggested solution.
P.S. Tess Ferrandez
, Microsoft Escalation Engineer describes
another drawback of using static members.