Rasshme
Rasshme

Reputation: 1641

Caching child user control only

There is parent user control, as seen below.

<%@ Control Language="C#" AutoEventWireup="true" CodeFile="TestUserControl.ascx.cs" Inherits="TestUserControl" %>
<%@ Register Src="~/UserControls/ChildUserControl.ascx" TagName="ChildUserControl" TagPrefix="FLI" %>
<div>    
    <FLI:ChildUserControl ID="child1" runat="server"/>    
</div>

The child usecontrol has pulic property MatchDescription, which is set in the Page_Load of the parent control. I want to cache multiple versions of the child control, based on the MatchDescription property.

Problem is, the MatchDescription property cant be set in Page_Load, as the cached copy of the child control is used once its available.

How can i fix this problem?

Thanks!

Upvotes: 1

Views: 462

Answers (1)

nick_w
nick_w

Reputation: 14938

It looks like using GetVaryByCustomString is the way to go here. My proof of concept consisted of the following:

  • WebUserControl.ascx: the test control. It has a single public property MatchDescription.
  • Global.asax: to override the GetVaryByCustomString method.
  • WebForm.aspx: a simple form to host the control.

WebUserControl.ascx

Add the following to the markup on the control:

<%@ OutputCache Duration="120" VaryByParam="none" VaryByCustom="MatchDescription" %>

This specifies the duration (in seconds) to cache the control and VaryByCustom="MatchDescription" specifies the name of the parameter we will be caching on.

WebUserControl.ascx.cs

public partial class WebUserControl1 : System.Web.UI.UserControl
{
    public string MatchDescription { get; set; }

    protected void Page_Load(object sender, EventArgs e)
    {
        object description = this.Context.Application["MatchDescription"];

        if (description != null)
        {
            this.MatchDescription = description.ToString();
        }
        else
        {
            this.MatchDescription = "Not set";
        }

        Label1.Text = "Match description: " + this.MatchDescription;
    }
}

This will check for the existance of the MatchDescription value. Because of the way the code in the parent page works, you should never see "Not set", though in your implementation it may be useful just in case the value is not set.

Global.asax

Add a Global.asax file to your project and add in the following method:

public override string GetVaryByCustomString(HttpContext context, string custom)
{
    if (custom == "MatchDescription")
    {
        object description = context.Application["MatchDescription"];

        if (description != null)
        {
            return description.ToString();
        }
    }

    return base.GetVaryByCustomString(context, custom);
}

This is the bit that checks for the MatchDescription associated with the cached control. If it is not found the control will be created as normal. context.Application is used because we need a way to communicate the description value between the parent page, the user control and the global.asax file.

WebForm.aspx.cs

public partial class WebForm : System.Web.UI.Page
{
    private static string[] _descriptions = new string[]
    {
        "Description 1",
        "Description 2",
        "Description 3",
        "Description 4"
    };

    protected override void OnPreInit(EventArgs e)
    {
        //Simulate service call.
        string matchDescription = _descriptions[new Random().Next(0, 4)];
        //Store description.
        this.Context.Application["MatchDescription"] = matchDescription;

        base.OnPreInit(e);
    }

    protected void Page_Load(object sender, EventArgs e)
    {
        var control = LoadControl("WebUserControl.ascx") as PartialCachingControl;
        this.Form.Controls.Add(control);

        //Indicate whether the control was cached.
        if (control != null)
        {
            if (control.CachedControl == null)
            {
                Label1.Text = "Control was cached";
            }
            else
            {
                Label1.Text = "Control was not cached";
            }
        }
    }
}

Note that in this code I am making/simulating the service call in the OnPreInit method. This is necessary as it occurs in the page lifecycle before the GetVaryByCustomString method.

Keep in mind that if a control has been cached, accessing it in the Page_Load method, for example, will require code of this form:

    if (control is PartialCachingControl &&
        ((PartialCachingControl)control).CachedControl =!= null)
{
    WebUserControl1 userControl = (WebUserControl1)((PartialCachingControl)control).CachedControl;
}

References:

My answer was inspired by: Any way to clear/flush/remove OutputCache?

I found the Pre_Init hint in this question: Output Caching - GetVaryByCustomString based on value set in PageLoad()

This KB article discusses why the PartialCachingControl.CachedControl property can always return null: http://support.microsoft.com/kb/837000

Upvotes: 1

Related Questions