簡體   English   中英

自定義WebControl的部分緩存

[英]Partial caching of custom WebControls

我需要緩存自定義WebControl的生成內容。 由於控制集合層次結構的構建非常昂貴,因此簡單地緩存數據庫結果是不夠的。 緩存整個頁面是不可行的,因為頁面內部還有其他動態部分。

我的問題:這個問題是否有最佳實踐方法? 我找到了很多緩存整個頁面或靜態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);

似乎是一個好主意,也許你應該注意:

  • 自定義控件的子控件的ClientIdMode,以防止在另一個上下文中顯示這些控件時發生沖突
  • 你的文字的LiteralMode:它應該是PassThrough
  • 緩存項目的到期模式(absoluteExpiration / slidingExpiration)
  • 禁用CustomControl的ViewState

最近,我傾向於采用另一種方法:我的包裝器控件只保存一些javascript,它在僅包含我的自定義控件的頁面上執行AJAX GET請求。 客戶端通過http頭和服務器端通過OutputCache指令執行緩存(除非HTTPS,內容必須公開)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM