Vary by control properties using PartialCaching in ASP.NET
Asked Answered
S

3

12

I am using the PartialCaching attribute on the base class of a user control.

I would like the cached controls to vary based on the properties set on the control instance.

For example:

<mycontrols:control1 runat="server" param1="10" param2="20" />

...output would be cached separately from a control instance with different properties:

<mycontrols:control1 runat="server" param1="15" param2="20" />

...and this control would be cached separately as well:

<mycontrols:control1 runat="server" param1="10" param2="25" />

However, if two control instances on two separate pages had identical param1 and param2 properties, I'd like them to cache as one object (so that cached control would be shared).

Can the above use case be achieved with PartialCaching attribute? What settings would I use? varyByControl?

Also, is it possible to make the cache duration variable at runtime?

Thanks.

Stegosaur answered 19/5, 2009 at 4:10 Comment(0)
D
22

To answer your first Q, let me first tell you that your question itself has the answer ;). 'Shared' ... yes that's the keyword :) To have a single instance in cache for the user control across all the pages, set Shared='true' in the @OutputCache directive. This should be set at the user control level i.e. in the ascx page.

To cache the user control based on user control properties, you should specify the fully qualified name of the properties in the varyByControls section of the PartialCachingAttribute. Multiple properties if any should be separated by semi-colons.

<%@ Control Language="C#" AutoEventWireup="true" 
CodeFile="WebUserControl.ascx.cs" 
Inherits="UC_WebUserControl" %>
<%@ OutputCache Duration="60" 
VaryByControl="UC_WebUserControl.param1;UC_WebUserControl.param2" 
VaryByParam="none" Shared="true" %>

or you can also include the PartialCache attribute for the user control:

[PartialCaching(60, null, "UC_WebUserControl.param1;UC_WebUserControl.param2", null, true)]
public partial class UC_WebUserControl : System.Web.UI.UserControl
{
    public string param1 { get; set; }
    public string param2 { get; set; }

}

OR another way to cache the control on the combination of both values would be:

[PartialCaching(60, null, "UC_WebUserControl.BothParams", null, true)]
public partial class UC_WebUserControl : System.Web.UI.UserControl
{
    public string param1 { get; set; }
    public string param2 { get; set; }

    public string BothParams    
    {
        get { return String.Concat(param1, param2); }
    }

}

The last parameter (true) specifies shared. Duration is specified by 60. Refer to the link How to: Cache Multiple Versions of a User Control Based on Parameters

To answer your second Q, to make the cache duration for the user control variable at run time, you can do it in two ways:

  1. Assign it in the user control code behind:

    [PartialCaching(60, null, "UC_WebUserControl.BothParams", null, true)]
    public partial class WebUserControl1 : System.Web.UI.UserControl
    {
        ...
        protected void Page_Load(object sender, EventArgs e)
        {
            this.CachePolicy.Duration = new TimeSpan(0, 0, 60);
        }    
    }
  2. You can assign it in the code behind of the page where user control is referenced using the ID of the user control.

e.g. If the user control on the aspx is:

<mycontrols:control1 ID="ucControl1" runat="server" param1="15" param2="20" />

then in the code behind of aspx, you should write:

this.ucControl1.CachePolicy.Duration = new TimeSpan(0, 0, 60);

FYI, if both the user control and page are cached: If the page output cache duration is less than that of a user control, the user control will be cached until its duration has expired, even after the remainder of the page is regenerated for a request. For example, if page output caching is set to 50 seconds and the user control's output caching is set to 100 seconds, the user control expires once for every two times the rest of the page expires.

Decreasing answered 24/5, 2009 at 9:35 Comment(6)
Response.Cache is for page-level caching. I'm interested in control-level caching using the PartialCaching attribute.Stegosaur
Also, your example [PartialCaching(60, "param1", null, null, true)] ...this will vary based on a "param1" QueryString or POST parameter. That's not what I want. "I would like the cached controls to vary based on the properties set on the control instance."Stegosaur
You need to assign the properties in VaryByControls of PartialCachingAttributeDecreasing
Edited my response accordinglyDecreasing
What if the user control was not declared staticly in the aspx file, what if it was loaded dynamically and the VaryByControl is set to a property on the user control: public long MessageID { get { return Convert.ToInt64(lblMessageID.Text); } set { lblMessageID.Text = value.ToString(); } } /// This creates a catch 22! Can't set the property until the control is loaded, can't load the correct one from cache because the property is not set.Recount
Heads up: this answer is incorrect. VaryByControl does not work with properties. I posted what I believe is correct below.Bacillary
C
1

What you need is Caching Multiple Versions of a User Control by Using Declarative Attributes.

Creditable answered 21/5, 2009 at 19:10 Comment(0)
B
1

I'm posting a new answer to this very old question because the accepted answer is woefully inaccurate. This correct answer is:

  • There is no built-in way to vary by control property value. VaryByControl only works for controls.
  • When a cached version is served, your control field will be null. You can't change the cache duration in code - you would get a NullReferenceException.
  • There is a bug that varies cache by control ID and NamingContainer ID(s) if VaryByControl is set to any value. That is why it appears to work sometimes. The bug is right here: http://referencesource.microsoft.com/#System.Web/xsp/system/Web/UI/PartialCachingControl.cs#530

I blogged about this recently right here: http://tabeokatech.blogspot.be/2014/09/outputcache-on-user-controls.html .

A way you could make this work is call the builder method for the PartialCachingControl yourself in code-behind, and embed the property value you want to vary by in the guid parameter:

    // PhControls is an asp:PlaceHolder
    protected void Page_Init(object sender, EventArgs e)
    {
        for (int i = 0; i < 3; i++)
        {
            System.Web.UI.StaticPartialCachingControl.BuildCachedControl(PhControls, String.Format("Test{0}", i), String.Format("1234567{0}", i), 180, null, null, null, null, new System.Web.UI.BuildMethod(GetBuilderDelegate(i)), null);
        }
    }

    public Func<Control> GetBuilderDelegate(int number)
    {
        return delegate()
        {
            var control = (UserControls.Test)LoadControl("~/UserControls/Test.ascx");
            control.Number = number;
            return control;
        };
    }

That neatly takes care of varying caching duration in code-behind as well. Make sure you remove the OutputCache directive from the markup in the ascx though when you do this. Otherwise the LoadControl gets you another PartialCachingControl and the cast fails.

Bacillary answered 12/9, 2014 at 19:35 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.