Telerik blogs

In some cases you may end up with the following exception when working with RadControls for ASP.NET Ajax:

"Script controls may not be registered after PreRender."

The explanation of this error is simple - some UI control implementing the IScriptControl interface (e.g. any control from the RadControls for ASP.NET Ajax suite) is stored in the Session, Application or Cache and then is added in some live controls collection. Here is an example:

<%@ Page Language="C#" %> 
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 
<script runat="server"
 
    protected void Page_Load(object sender, EventArgs e) 
    { 
        Timer timer = (Timer)Session["Timer"]; 
        if (timer == null) 
        { 
            timer = new Timer { ID = "Timer1" }; 
            Session["Timer"] = timer; 
        } 
 
        PlaceHolder1.Controls.Add(timer); 
        Response.Write(timer.Page == this); 
    } 
</script> 
 
<html xmlns="http://www.w3.org/1999/xhtml"
<head runat="server"
    <title>Caching issues</title> 
</head> 
<body> 
    <form id="form1" runat="server"
        <asp:ScriptManager runat="server" ID="ScriptManager1" /> 
        <asp:PlaceHolder runat="server" ID="PlaceHolder1" /> 
        <asp:Button Text="Postback" ID="Button1" runat="server" /> 
    </form> 
</body> 
</html> 

Here a Timer control is stored in session state thus avoiding its creation on every page load. Sounds quite reasonable, doesn't it? Unfortunately not. If you load this page and then press the "Postback" button you will end up with the yellow screen of death saying that "Script controls may not be registered after PreRender". But why? The answer is simple - caching. The Timer control (as well as most IScriptControls) caches a reference to the current ScriptManager (current means "from the current Page"). Here is how:

internal ScriptManager ScriptManager 
    get 
    { 
        if (this._scriptManager == null
        { 
            Page page = this.Page; 
            this._scriptManager = ScriptManager.GetCurrent(page); 
        } 
        return this._scriptManager; 
    } 

During the first load of the sample page everything is fine. The ScriptManager property is properly initialized and the page runs happily. After postback the Timer control is pulled out from session state. As caching is implemented the _scriptManager field will not be null and the Timer control will try to register with an old ScriptManager. However the life cycle of the page object, to which that "old" ScriptManager belonged, has been executed during the previous request(including the PreRender event). When the Timer control tries to register with the ScriptManager we end up with the exception that the PreRender method has already been executed - and it really was during the first request to the page.

Although this exception is specific to ASP.NET Ajax enabled controls you should avoid caching *any* ASP.NET controls at all costs. Tess Ferrandez from Microsoft has a series of blog posts telling why storing UI controls is not a good idea:

http://blogs.msdn.com/tess/archive/2008/05/28/asp-net-memory-thou-shalt-not-store-ui-objects-in-cache-or-session-scope.aspx

http://blogs.msdn.com/tess/archive/2006/01/23/net-memory-leak-case-study-the-event-handlers-that-made-the-memory-baloon.aspx

http://blogs.msdn.com/tess/archive/2008/09/12/asp-net-memory-issues-high-memory-usage-with-ajaxpro.aspx

But if this is not a proper way of caching ASP.NET UI controls what is? Cache the data used to populate the control instead. Or use output caching.

I hope this helps,


About the Author

Atanas Korchev

 is Team Leader in Kendo UI Team

Related Posts

Comments

Comments are disabled in preview mode.