简体   繁体   English

自定义WebControl的部分缓存

[英]Partial caching of custom WebControls

I need to cache the generated content of custom WebControls. 我需要缓存自定义WebControl的生成内容。 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. 我找到了很多缓存整个页面或静态UserControls的解决方案,但对我来说并不合适。 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: 应该缓存的自定义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));
    }
}

The aspx page which includes the content control(s) could look as follows: 包含内容控件的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;
    }
}

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). 我最终编写了两个包装器控件,一个用于捕获生成的html,另一个用于缓存内容 - 相当多的代码用于简单的缓存功能(见下文)。

The wrapper control for capturing the output overwrites the function Render and looks like this: 用于捕获输出的包装器控件将覆盖函数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);
    }
}

The wrapper control to cache this generated output looks as follows: 用于缓存此生成的输出的包装器控件如下所示:

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);
    }
}

In my aspx page i replaced 在我的aspx页面中,我更换了

// This controls needs to be cached
form1.Controls.Add(new ReportControl() { ReportViewModel = reportContent });

with

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);

Seems like a good idea, maybe you should pay attention to : 似乎是一个好主意,也许你应该注意:

  • the ClientIdMode of the child controls of your custom control to prevent conflicts if these controls are to be displayed in another context 自定义控件的子控件的ClientIdMode,以防止在另一个上下文中显示这些控件时发生冲突
  • the LiteralMode of your Literal : it should be PassThrough 你的文字的LiteralMode:它应该是PassThrough
  • the expiration mode of your cached item (absoluteExpiration/slidingExpiration) 缓存项目的到期模式(absoluteExpiration / slidingExpiration)
  • disable ViewState of your CustomControl 禁用CustomControl的ViewState

Recently, I tend to have another approach : my wrapper controls only holds some javascript that performs an AJAX GET request on a page containing only my custom control. 最近,我倾向于采用另一种方法:我的包装器控件只保存一些javascript,它在仅包含我的自定义控件的页面上执行AJAX GET请求。 Caching is performed client side through http headers and serverside through OutputCache directive (unless HTTPS, content has to be public though) 客户端通过http头和服务器端通过OutputCache指令执行缓存(除非HTTPS,内容必须公开)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM