WEBSITE

ASP.NET 3.5 : Caching ASP.NET Pages (part 3) - Caching Portions of ASP.NET Pages

2/18/2013 6:30:00 PM

4. Caching Portions of ASP.NET Pages

The capability of caching the output of Web pages adds a lot of power to your programming arsenal, but sometimes caching the entire content of a page is not possible or it’s just impractical. Some pages, in fact, are made of pieces with radically different features as far as cacheability is concerned. In these situations, being able to cache portions of a page is an incredible added value.

If caching the entire page is impractical, you can always opt for partial caching. Partial caching leverages the concept of ASP.NET user controls—that is, small, nested pages that inherit several features of the page. In particular, user controls can be cached individually based on the browser, GET and POST parameters, and the value of a particular set of properties.

What’s a User Control, Anyway?

A user control is a Web form saved to a distinct file with an .ascx extension. The similarity between user controls and pages is not coincidental. You create a user control in much the same way you create a Web form, and a user control is made of any combination of server and client controls sewn together with server and client script code. Once created, the user control can be inserted in an ASP.NET page like any other server control. ASP.NET pages see the user control as an atomic, encapsulated component and work with it as with any other built-in Web control.

The internal content of the user control is hidden to the host page. However, the user control can define a public programming interface and filter access to its constituent controls via properties, methods, and events.

User controls and pages have so much in common that transforming a page, or a part of it, into a user control is no big deal. You copy the portion of the page of interest to a new .ascx file and make sure the user control does not contain any of the following tags: <html>, <body>, or <form>. You complete the work by associating a code-behind file (or a <script runat=”server”> block) to code the expected behavior of the user control. Finally, you add a @Control directive in lieu of the @Page directive. Here’s an example of a user control:

<%@ Control Language="C#" CodeFile="Message.ascx.cs" Inherits="Message" %>
<span style="color:<%= ForeColor%>"><%= Text%></span>

Here’s the related code-behind class:

public partial class Message : System.Web.UI.UserControl
{
    public string ForeColor;
    public string Text;
}

To insert a user control into an ASP.NET page, you drag it from the project onto the Web form, when in design mode. Visual Studio .NET registers the user control with the page and prepares the environment for you to start adding code.

<%@ Page Language="C#" CodeFile="Test.aspx.cs" Inherits="TestUserCtl" %>
<%@ Register Src="Message.ascx" TagName="Message" TagPrefix="x" %>
<html><body>
    <form id="form1" runat="server">
        <x:Message ID="Message1" runat="server" />
    </form>
</body></html>

In the page code-behind class, you work the Message1 variable as you would do with any other server control:

protected void Page_Load(object sender, EventArgs e)
{
    Message1.ForeColor = "blue";
    Message1.Text = "Hello world";
}

Caching the Output of User Controls

User controls are not only good at modularizing your user interface, they’re also great at caching portions of Web pages. User controls, therefore, fully support the @OutputCache directive, although they do so with some differences with ASP.NET pages, as outlined in Table 16-7.

A page that contains some dynamic sections cannot be cached entirely. What if the page also contains sections that are both heavy to compute and seldom updated? In this case, you move static contents to one or more user controls and use the user control’s @OutputCache directive to set up output caching.

To make a user control cacheable, you declare the @OutputCache attribute using almost the same set of attributes we discussed earlier for pages. For example, the following code snippet caches the output of the control that embeds it for one minute:

<% @OutputCache Duration="60" VaryByParam="None" %>

The Location attribute is not supported because all controls in the page share the same location. So if you need to specify the cache location, do that at the page level and it will work for all embedded controls. The same holds true for the VaryByHeader attribute.

The output of a user control can vary by custom strings and form parameters. More often, though, you’ll want to vary the output of a control by property values. In this case, use the new VaryByControl attribute.

Vary by Controls

The VaryByControl attribute allows you to vary the cache for each specified control property. For user controls, the property is mandatory unless the VaryByParam attribute has been specified. You can indicate both VaryByParam and VaryByControl, but at least one of them is required.

The following user control displays a grid with all the customers in a given country. The country is specified by the user control’s Country property.

<%@ Control Language="C#" CodeFile="CustomersGrid.ascx.cs"
            Inherits="CustomersGridByCtl" %>
<%@ OutputCache Duration="30" VaryByControl="Country" %>

<h3><%= DateTime.Now.ToString() %></h3>
<asp:ObjectDataSource ID="ObjectDataSource1" runat="server"
    TypeName="Core35.DAL.Customers"
    SelectMethod="LoadByCountry">
</asp:ObjectDataSource>

<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="false">
    <Columns>
        <asp:BoundField DataField="ID" HeaderText="ID" />
        <asp:BoundField DataField="CompanyName" HeaderText="Company" />
        <asp:BoundField DataField="ContactName" HeaderText="Contact" />
        <asp:BoundField DataField="Country" HeaderText="Country" />
    </Columns>
</asp:GridView>

Here is the code file of the user control:

public partial class CustomersGridByCtl : System.Web.UI.UserControl
{
    public string Country;

    protected void Page_Load(object sender, EventArgs e)
    {
        if (!String.IsNullOrEmpty(Country))
        {
            ObjectDataSource1.SelectParameters.Add("country", Country);
            GridView1.DataSourceID = "ObjectDataSource1";
        }
    }
}

The @OutputCache directive caches a different copy of the user control output based on the different values of the Country property. Figure 2 shows it in action.

Figure 2. Two pages created in different moments use the same user control output, as you can see from the creation time of the grid.

The strings you assign to VaryByControl can be properties of the user controls as well as ID property values for contained controls. In this case, you’ll get a distinct cached copy for each distinct combination of property values on the specified control.

The Shared Attribute

In Figure 16-8, you see two instances of the same page sharing the cached output of a user control. Try the following simple experiment. Make a plain copy of the page (say, page1.aspx) and give it another name (say, page2.aspx). You should have two distinct pages that generate identical output. In particular, both pages contain an instance of the same cacheable user control. Let’s say that the cache duration of the user control is 30 seconds.

As the next step of the experiment, you open both pages at different times while the cache is still valid. Let’s say you open the second page ten seconds later than the first. Interestingly enough, the two pages are no longer sharing the same copy of user control output, as Figure 3 documents.

Figure 3. Distinct pages don’t automatically share the output of the same user control.

By default, distinct pages don’t share the output of the same cacheable user control. Each page will maintain its own copy of the user control response, instead. Implemented to guarantee total separation and avoid any sort of conflicts, this feature is far more dangerous than one might think at first. It might lead to flooding the Web server memory with copies and copies of the user control responses—one for each varying parameter or control property and one set for each page that uses the control.

To allow distinct pages to share the same output of a common user control, you need to set the Shared attribute to true in the user control’s @OutputCache directive:

<%@ OutputCache Duration="30" VaryByParam="None" Shared="true" %>

Tip

To avoid memory problems, you should put a limit on the total amount of memory available to IIS. It is set to 60 percent of the physical memory, but you should keep it under 800 MB per Microsoft recommendations. Setting the IIS 6.0 Maximum Used Memory parameter is important especially if output cache is used aggressively. You’ll set the parameter on a per-application-pool basis by selecting the IIS 6.0 application pool where your application is configured to run and opening its Properties dialog box.


Fragment Caching in Cacheable Pages

If you plan to cache user controls—that is, you’re trying for partial caching—it’s probably because you just don’t want to, or cannot, cache the entire page. However, a good question to ask is: what happens if user controls are cached within a cacheable page?

Both the page and the controls are cached individually, meaning that both the page’s raw response and the control’s raw responses are cached. However, if the cache duration is different, the page duration wins and user controls are refreshed only when the page is refreshed.

A cacheable user control can be embedded both in a cacheable page and in a wrapper cacheable user control.

Warning

Cacheable user controls should be handled with extreme care in the page’s code. Unlike regular controls, a user control marked with the @OutputCache directive is not guaranteed to be there when your code tries to access it. If the user control is retrieved from the cache, the property that references it in the code-behind page class is just null.

if (CustomerGrid1 != null)
   CustomerGrid1.Country = "USA";

To avoid bad surprises, you should always check the control reference against the null value before executing any code.

Other  
 
Top 10
Review : Sigma 24mm f/1.4 DG HSM Art
Review : Canon EF11-24mm f/4L USM
Review : Creative Sound Blaster Roar 2
Review : Philips Fidelio M2L
Review : Alienware 17 - Dell's Alienware laptops
Review Smartwatch : Wellograph
Review : Xiaomi Redmi 2
Extending LINQ to Objects : Writing a Single Element Operator (part 2) - Building the RandomElement Operator
Extending LINQ to Objects : Writing a Single Element Operator (part 1) - Building Our Own Last Operator
3 Tips for Maintaining Your Cell Phone Battery (part 2) - Discharge Smart, Use Smart
REVIEW
- First look: Apple Watch

- 3 Tips for Maintaining Your Cell Phone Battery (part 1)

- 3 Tips for Maintaining Your Cell Phone Battery (part 2)
VIDEO TUTORIAL
- How to create your first Swimlane Diagram or Cross-Functional Flowchart Diagram by using Microsoft Visio 2010 (Part 1)

- How to create your first Swimlane Diagram or Cross-Functional Flowchart Diagram by using Microsoft Visio 2010 (Part 2)

- How to create your first Swimlane Diagram or Cross-Functional Flowchart Diagram by using Microsoft Visio 2010 (Part 3)
Popular Tags
Video Tutorail Microsoft Access Microsoft Excel Microsoft OneNote Microsoft PowerPoint Microsoft Project Microsoft Visio Microsoft Word Active Directory Exchange Server Sharepoint Sql Server Windows Server 2008 Windows Server 2012 Windows 7 Windows 8 Adobe Flash Professional Dreamweaver Adobe Illustrator Adobe Photoshop CorelDRAW X5 CorelDraw 10 windows Phone 7 windows Phone 8 Iphone