Wednesday, December 17, 2008

ToolkitScriptManager - Heavy Use of Reflection & Poor Performance

I work on a project that was using the ToolkitScriptManager control from the AjaxControlToolkit. The control was introduced as a way to minimize the number of browser requests for javascript resources in an ASP.NET AJAX application;

'This control allows you to replace the default control behavior, and supports the ability to dynamically merge multiple client-side Javascript scripts into a single file that is downloaded to the client at runtime.'

How does this control achieve this behavior? Reflection, of course!

‘Its usage is simple: you add the ScriptCombineAttribute attribute to the assembly that contains the JS files you want to be combined and add a ToolkitScriptManager control to the page (making sure that the CombineScripts property of the control is set to true - which is the default).’

The control looks promising but dropping this control on your master page and firing up the profiler might surprise you, it sure surprised us;


The control works by overriding the OnResolveScriptReference method, keep in mind this method is invoked every time a page encounters a script reference. The override loads the containing assembly and then calls the reflective method GetCustomAttributes to discover scripts to combine. There is no caching done whatsoever. The end result is a very heavyweight control that (ironically) slows down your pages and more than likely erases the performance benefits of script combining in the first place. In our system the control consumed 90%+ of the total processing time of pages with no LDAP or database calls, around 40% of the processing time otherwise.

I was watching an archived video feed of a PDC presentation by Scott Hunter and noticed that the AjaxControlToolkit has apparently been 'promoted' and will ship as part of ASP.NET 4. I doubt the ToolkitScriptManager will be in the final bits however since the base ScriptManager control got the same script combining functionality added to it in ASP.NET 3.5 SP1. It works a little differently but evidently there is no use of reflection.