Мне нужно кэшировать сгенерированный контент пользовательских WebControls.Поскольку построение иерархии коллекций элементов управления очень дорого, простого кэширования результатов базы данных недостаточно.Кэширование всей страницы неосуществимо, поскольку внутри страницы есть другие динамические части.
Мой вопрос: существует ли передовой метод решения этой проблемы?Я нашел много решений, кеширующих целые страницы или статические UserControls, но ничего подходящего для меня.Я закончил с моим собственным решением, но я сомневаюсь, что это выполнимый подход.
Пользовательский WebControl, который должен быть кэширован, может выглядеть так:
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));
}
}
Страница aspx, котораявключает в себя элементы управления контентом может выглядеть следующим образом:
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;
}
}
В итоге я написал два элемента управления оболочки, один для захвата сгенерированного HTML и второй для кэширования содержимого - достаточно много кода для простогофункция кэширования (см. ниже).
Элемент управления оболочки для захвата вывода перезаписывает функцию Render и выглядит следующим образом:
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);
}
}
Элемент управления оболочки для кэширования этого сгенерированного вывода выглядит следующим образом:
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);
}
}
На моей странице aspx я заменил
// This controls needs to be cached
form1.Controls.Add(new ReportControl() { ReportViewModel = reportContent });
на
CachingControlWrapper cachingControlWrapper = new CachingControlWrapper();
// CachingKey - Each Report must be cached independently
cachingControlWrapper.CachingKey = "ReportControl_" + reportID;
// Create Control Delegate - Control to cache, generated only if control does not exist in cache
cachingControlWrapper.CreateControl = () => { return new ReportControl() { ReportViewModel = reportContent }; };
form1.Controls.Add(cachingControlWrapper);