Excluding user controls from OutputCache in a Sitefinity Site
I have an ASP.NET site built on the Sitefinity CMS. The vast majority of the site is built using custom webforms user controls. Sitefinity is a good platform for managing content and all that jazz, but if you're not using it's outputCache functionality (which I believe is really just ASP.NET's outputCache functionality) your performance is going to blow goats.
You can't easily turn on/off caching for individual user control's on a page. I tried the @outputCache attribute in the ascx page, setting the partialCaching attribute on the class in the code behind, as well as programatically defining the partialCache control as explained here (http://msdn.microsoft.com/en-us/library/system.web.ui.controlcachepolicy.aspx). None of it worked. I could have utilized the SetVaryByCustom property on the cache to create custom cached pages for certain values but there were too many potential values. What good is an cache if every user has to have their own version of a page in the said cache?
The only solution that seemed like it would work is using the ASP substitution control (http://msdn.microsoft.com/en-us/library/ms228212.aspx). This control is pretty neat. You drop it in the markup in your ascx and when the page is requested, even if it is cached, it will run a static method that returns a string. But how is this supposed to help me substitute an entire control? It would be super dirty for me to just put all my html markup in a string and return that. Luckily ASP.NET allows you to render pages and controls to strings.
Build your control as you normally would. Then, create a "container" control that is going to render the real control. This container control is what should be added to Sitefinity. In the markup for the control add the following:
<asp:Substitution ID="Substitution1" runat="server"
MethodName="RenderMyControl" />
Now in your code behind add the following method:
protected static string RenderMyControl(HttpContext context)
{
Page pageHolder = new Page();
MyControl viewControl =
(MyControl)pageHolder.LoadControl(
"/controls/MyControl.ascx");
pageHolder.Controls.Add(viewControl);
StringWriter result = new StringWriter();
HttpContext.Current.Server.Execute(pageHolder, result, false);
return result.ToString();
}
And that is it. Now your control will be substituted on every page load. Props to Jon Kragh (@jonkragh) for this blog post that helped me find the solution for how to properly render a control to a string.
http://www.jonkragh.com/index.php/rendering-an-asp-net-usercontrol-to-a-string/
His post also users generics so that you could pass in any control to be rendered as a string. Note that this solution is superior to just just instantiating an instance of your control and then calling the RenderControl method. If you go that route page life-cycle events like pageload and pageinit won't be fired and you won't be able to bind data to templates in your markup.