Asp.net 自定义网络控件的部分缓存

Asp.net 自定义网络控件的部分缓存,asp.net,caching,web-controls,Asp.net,Caching,Web Controls,我需要缓存自定义网络控件生成的内容。由于控件集合层次结构的构建非常昂贵,因此仅对数据库结果进行简单缓存是不够的。缓存整个页面是不可行的,因为页面中还有其他动态部分 我的问题是:对于这个问题有没有最佳实践方法?我发现了很多缓存整个页面或静态用户控件的解决方案,但没有适合我的。我最终提出了自己的解决方案,但我很怀疑这是否可行 应该缓存的自定义WebControl可能如下所示: public class ReportControl : WebControl { public string Re

我需要缓存自定义网络控件生成的内容。由于控件集合层次结构的构建非常昂贵,因此仅对数据库结果进行简单缓存是不够的。缓存整个页面是不可行的,因为页面中还有其他动态部分

我的问题是:对于这个问题有没有最佳实践方法?我发现了很多缓存整个页面或静态用户控件的解决方案,但没有适合我的。我最终提出了自己的解决方案,但我很怀疑这是否可行

应该缓存的自定义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));
    }
}
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);
    }
}
包含内容控件的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;
    }
}
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);
    }
}
最后,我编写了两个包装器控件,一个用于捕获生成的html,另一个用于缓存内容——相当多的代码用于简单的缓存功能(见下文)

用于捕获输出的包装器控件覆盖函数Render,如下所示:

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


似乎是个好主意,也许你应该注意:

  • 自定义控件的子控件的ClientIdMode,以防止在其他上下文中显示这些控件时发生冲突
  • 文本的LiteralMode:应该是PassThrough
  • 缓存项的过期模式(绝对过期/滑动过期)
  • 禁用CustomControl的ViewState
最近,我倾向于采用另一种方法:我的包装器控件只包含一些javascript,这些javascript在只包含自定义控件的页面上执行AJAX GET请求。
缓存通过http头在客户端执行,通过OutputCache指令在服务器端执行(除非HTTPS,但内容必须是公共的)

Set Page指令<代码>根据reportID或
对于所有查询字符串参数,如上所述,我无法缓存整个页面。好的评论-我会考虑它们。因此,您可以为每个控件缓存一个页面——客户端通过JS调用页面内容?是的,在正确的上下文中使用,它有助于跨应用程序、服务器和第三方站点共享控件(如导航控件)。这减少了带宽和服务器开销。听起来符合您的要求。就我的特殊情况来说,这似乎有点间接。我必须使服务器端调用和带有缓存控件的页面无法直接从客户端访问。如果我的UserCanAccessReport为true,我将进行调用,并可以使用返回的内容进行LiteralControl。不过,谢谢你的意见。