Partial caching of custom WebControls

treehuggingirl

New Member
I need to cache the generated content of custom WebControls. Since build up of control collection hierarchy is very expensive, simple caching of database results is not sufficient. Caching the whole page is not feasible, because there are other dynamic parts inside the page.My Question: Is there a best practice approach for this problem? I found a lot of solutions caching whole pages or static UserControls, but nothing appropriate for me. I ended up with my own solution, but im quite doubtful if this is a feasible approach.A custom WebControl which should be cached could look like this:\[code\]public class ReportControl : WebControl{ public string ReportViewModel { get; set; } protected override void OnLoad(EventArgs e) { base.OnLoad(e); // Fake expensive control hierarchy build up System.Threading.Thread.Sleep(10000); this.Controls.Add(new LiteralControl(ReportViewModel)); }}\[/code\]The aspx page which includes the content control(s) could look as follows:\[code\]public partial class Default : System.Web.UI.Page{ protected void Page_Load(object sender, EventArgs e) { // Fake authenticated UserID int userID = 1; // Parse ReportID int reportID = int.Parse(Request.QueryString["ReportID"]); // Validate if current user is allowed to view report if (!UserCanAccessReport(userID, reportID)) { form1.Controls.Add(new LiteralControl("You're not allowed to view this report.")); return; } // Get ReportContent from Repository string reportContent = GetReport(reportID); // This controls needs to be cached form1.Controls.Add(new ReportControl() { ReportViewModel = reportContent }); } private bool UserCanAccessReport(int userID, int reportID) { return true; } protected string GetReport(int reportID) { return "This is Report #" + reportID; }}\[/code\]I ended up writing two wrapper controls, one for capturing generated html and a second one for caching the content - Quite a lot of code for simple caching functionality (see below).The wrapper control for capturing the output overwrites the function Render and looks like this:\[code\]public class CaptureOutputControlWrapper : Control{ public event EventHandler OutputGenerated = (sender, e) => { }; public string CapturedOutput { get; set; } public Control ControlToWrap { get; set; } protected override void OnLoad(EventArgs e) { base.OnLoad(e); this.Controls.Add(ControlToWrap); } protected override void Render(HtmlTextWriter writer) { StringWriter stringWriter = new StringWriter(); HtmlTextWriter htmlTextWriter = new HtmlTextWriter(stringWriter); base.RenderChildren(htmlTextWriter); CapturedOutput = stringWriter.ToString(); OutputGenerated(this, EventArgs.Empty); writer.Write(CapturedOutput); }}\[/code\]The wrapper control to cache this generated output looks as follows:\[code\]public class CachingControlWrapper : WebControl{ public CreateControlDelegate CreateControl; public string CachingKey { get; set; } public delegate Control CreateControlDelegate(); protected override void OnLoad(EventArgs e) { base.OnLoad(e); string content = HttpRuntime.Cache.Get(CachingKey) as string; if (content != null) { // Content is cached, display this.Controls.Add(new LiteralControl(content)); } else { // Content is not cached, create specified content control and store output in cache CaptureOutputControlWrapper wrapper = new CaptureOutputControlWrapper(); wrapper.ControlToWrap = CreateControl(); wrapper.OutputGenerated += new EventHandler(WrapperOutputGenerated); this.Controls.Add(wrapper); } } protected void WrapperOutputGenerated(object sender, EventArgs e) { CaptureOutputControlWrapper wrapper = (CaptureOutputControlWrapper)sender; HttpRuntime.Cache.Insert(CachingKey, wrapper.CapturedOutput); }}\[/code\]In my aspx page i replaced\[code\]// This controls needs to be cachedform1.Controls.Add(new ReportControl() { ReportViewModel = reportContent });\[/code\]with\[code\]CachingControlWrapper cachingControlWrapper = new CachingControlWrapper();// CachingKey - Each Report must be cached independentlycachingControlWrapper.CachingKey = "ReportControl_" + reportID;// Create Control Delegate - Control to cache, generated only if control does not exist in cachecachingControlWrapper.CreateControl = () => { return new ReportControl() { ReportViewModel = reportContent }; };form1.Controls.Add(cachingControlWrapper);\[/code\]
 
Top