ASP.NET 2.0 Unleashed

In the previous section of this chapter, you learned how to cache the entire output of a page. In this section, you learn how to take advantage of Partial Page Caching to cache particular regions of a page.

Partial Page Caching makes sense when a page contains both dynamic and static content. For example, you might want to cache a set of database records displayed in a page, but not cache a random list of news items displayed in the same page.

In this section, you learn about two methods for enabling Partial Page Caching. You can use post-cache substitution to cache an entire page except for a particular region. You can use User Controls to cache particular regions in a page, but not the entire page.

Using Post-Cache Substitution

In some cases, you might want to cache an entire page except for one small area. For example, you might want to display the current username dynamically at the top of a page, but cache the remainder of a page. In these cases, you can take advantage of a feature of the ASP.NET Framework called post-cache substitution.

Post-cache substitution is used internally by the AdRotator control. Even when you use Page Output Caching to cache a page that contains an AdRotator control, the content rendered by the AdRotator control is not cached.

You can use post-cache substitution either declaratively or programmatically. If you want to use post-cache substitution declaratively, then you can use the ASP.NET Substitution control. For example, the page in Listing 23.18 uses the Substitution control to display the current time on a page that has been output cached (see Figure 23.6).

Figure 23.6. Using the Substitution control.

Listing 23.18. SubstitutionControl.aspx

<%@ Page Language="VB" %> <%@ OutputCache Duration="15" VaryByParam="none" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <script runat="server"> Public Shared Function GetTime(ByVal context As HttpContext) As String Return DateTime.Now.ToString("T") End Function </script> <html xmlns="http://www.w3.org/1999/xhtml" > <head runat="server"> <title>Substitution Control</title> </head> <body> <form runat="server"> <div> The cached time is: <%= DateTime.Now.ToString("T") %> <hr /> The substitution time is: <asp:Substitution MethodName="GetTime" Runat="server" /> </div> </form> </body> </html>

In Listing 23.18, the time is displayed twice. The time displayed in the body of the page is output cached. The time displayed by the Substitution control is not cached.

The Substitution control has one important property: MethodName. The MethodName property accepts the name of a method defined in the page. The method must be a shared (static) method because an instance of the class is not created when the page is output cached.

Alternatively, you can use post-cache substitution programmatically by using the Response.WriteSubstitution() method. This method is illustrated in the page in Listing 23.19.

Listing 23.19. ShowWriteSubstitution.aspx

<%@ Page Language="VB" %> <%@ OutputCache Duration="15" VaryByParam="none" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <script runat="server"> Public Shared Function GetTime(ByVal context As HttpContext) As String Return DateTime.Now.ToString("T") End Function </script> <html xmlns="http://www.w3.org/1999/xhtml" > <head runat="server"> <title>Show WriteSubstitution</title> </head> <body> <form runat="server"> <div> The cached time is: <%= DateTime.Now.ToString("T") %> <hr /> The substitution time is: <% Response.WriteSubstitution(AddressOf GetTime)%> </div> </form> </body> </html>

There are two advantages to using the WriteSubstitution() method. First, the method referenced by the WriteSubstitution() method does not have to be a method of the current class. The method can be either an instance or shared method on any class.

The second advantage of the WriteSubstitution() method is that you can use it within a custom control to perform post-cache substitutions. For example, the NewsRotator control in Listing 23.20 uses the WriteSubstitution() method when displaying a random news item. If you use this control in a page that has been output cached, the NewsRotator control continues to display news items randomly.

Listing 23.20. NewsRotator.vb

Imports System Imports System.Data Imports System.Web Imports System.Web.UI Imports System.Web.UI.WebControls Imports System.Collections.Generic Namespace myControls Public Class NewsRotator Inherits WebControl Public Shared Function GetNews(ByVal context As HttpContext) As String Dim News As New List(Of String)() News.Add("Martians attack!") News.Add("Moon collides with earth!") News.Add("Life on Jupiter!") Dim rnd As Random = New Random() Return News(rnd.Next(News.Count)) End Function Protected Overrides Sub RenderContents(ByVal writer As HtmlTextWriter) Context.Response.WriteSubstitution(AddressOf GetNews) End Sub End Class End Namespace

Note

Building custom controls is discussed in detail in Chapter 31, "Building Custom Controls."

The CD that accompanies this book includes a page named ShowNewsRotator.aspx. If you open this page, all the content of the page is cached except for the random news item displayed by the NewsRotator control (see Figure 23.7).

Figure 23.7. Displaying dynamic news items in a cached page.

When you use post-cache substitution (declaratively or programmatically) then caching no longer happens beyond the web server. Using post-cache substitution causes a Cache-Control:no-cache HTTP header to be included in the HTTP response, which disables caching on proxy servers and browsers. This limitation is understandable because the substitution content must be generated dynamically with each page request.

Caching with a User Control

Using post-cache substitution is appropriate only when working with a string of text or HTML. If you need to perform more complex partial page caching, then you should take advantage of User Controls.

You can cache the rendered contents of a User Control in memory in the same way as you can cache an ASP.NET page. When you add an <%@ OutputCache %> directive to a User Control, the rendered output of the User Control is cached.

Note

When you cache a User Control, the content is cached on the web server and not on any proxy servers or web browsers. When a web browser or proxy server caches a page, it always caches an entire page.

For example, the Movies User Control in Listing 23.21 displays all the rows from the Movies database table. Furthermore, it includes an OutputCache directive, which causes the contents of the User Control to be cached in memory for a maximum of 10 minutes (600 seconds).

Listing 23.21. Movies.ascx

<%@ Control Language="VB" ClassName="Movies" %> <%@ OutputCache Duration="600" VaryByParam="none" %> User Control Time: <%= DateTime.Now.ToString("T") %> <asp:GridView DataSource Runat="server" /> <asp:SqlDataSource ConnectionString="<%$ ConnectionStrings:Movies %>" SelectCommand="SELECT Title,Director FROM Movies" Runat="server" />

The User Control in Listing 23.21 displays the records from the Movies database table with a GridView control. It also displays the current time. Because the control includes an OutputCache directive, the entire rendered output of the control is cached in memory.

The page in Listing 23.22 includes the Movies User Control in the body of the page. It also displays the current time at the top of the page. When you refresh the page, the time displayed by the Movies control changes, but not the time displayed in the body of the page (see Figure 23.8).

Figure 23.8. Caching the output of a User Control.

Listing 23.22. ShowUserControlCache.aspx

<%@ Page Language="VB" %> <%@ Register TagPrefix="user" TagName="Movies" src="/books/3/444/1/html/2/~/Movies.ascx" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" > <head runat="server"> <title>Show User Control Cache</title> </head> <body> <form runat="server"> <div> Page Time: <%= DateTime.Now.ToString("T") %> <hr /> <user:Movies Runat="server" /> </div> </form> </body> </html>

You can use the following attributes with an <%@ OutputCache %> directive declared in a User Control:

  • Duration The amount of time in seconds that the rendered content of the User Control is cached.

  • Shared Enables you to share the same cached version of the User Control across multiple pages.

  • VaryByParam Enables you to create different cached versions of a User Control, depending on the values of one or more query string or form parameters. You can specify multiple parameters by supplying a semicolon-delimited list of query string or form parameter names.

  • VaryByControl Enables you to create different cached versions of a User Control, depending on the value of a control. You can specify multiple controls by supplying a semicolon-delimited list of control IDs.

  • VaryByCustom Enables you to specify a custom string used by a custom cache policy. (You also can supply the special value browser, which causes different cached versions of the control to be created when the type and major version of the browser differs.)

Because each User Control that you add to a page can have different caching policies, and because you can nest User Controls with different caching policies, you can build pages that have fiendishly complex caching policies. There is nothing wrong with doing this. In fact, you should take advantage of this caching functionality whenever possible to improve the performance of your applications.

Warning

Be careful when setting properties of a cached User Control. If you attempt to set the property of a User Control programmatically when the content of the control is served from the cache, you get a NullReference exception. Before setting a property of a cached control, first check whether the control actually exists like this:

If Not IsNothing(myControl) Then myControl.SomeProperty = "some value" End If

Sharing a User Control Output Cache

By default, instances of the same User Control located on different pages do not share the same cache. For example, if you add the same Movies User Control to more than one page, then the contents of each user control is cached separately.

If you want to cache the same User Control content across multiple pages, then you need to include the Shared attribute when adding the <%@ OutputCache %> directive to a User Control. For example, the modified Movies User Control in Listing 23.23 includes the Shared attribute.

Listing 23.23. SharedMovies.ascx

<%@ Control Language="VB" ClassName="SharedMovies" %> <%@ OutputCache Duration="600" VaryByParam="none" Shared="true" %> User Control Time: <%= DateTime.Now.ToString() %> <asp:GridView DataSource Runat="server" /> <asp:SqlDataSource ConnectionString="<%$ ConnectionStrings:Movies %>" SelectCommand="SELECT Title,Director FROM Movies" Runat="server" />

Using the Shared attribute is almost always a good idea. You can save a significant amount of server memory by taking advantage of this attribute.

Manipulating a User Control Cache Programmatically

When you include an <%@ OutputCache %> directive in a User Control, you can modify programmatically how the User Control is cached. The User Control CachePolicy property exposes an instance of the ControlCachePolicy class, which supports the following properties:

  • Cached Enables you to enable or disable caching.

  • Dependency Enables you to get or set a cache dependency for the User Control.

  • Duration Enables you to get or set the amount of time (in seconds) that content is cached.

  • SupportsCaching Enables you to check whether the control supports caching.

  • VaryByControl Enables you to create different cached versions of the control, depending on the value of a control.

  • VaryByParams Enables you to create different cached versions of the control, depending on the value of a query string or form parameter.

The ControlCachePolicy class also supports the following methods:

  • SetExpires Enables you to set the expiration time for the cache.

  • SetSlidingExpiration Enables you to set a sliding expiration cache policy.

  • SetVaryByCustom Enables you to specify a custom string used by a custom cache policy. (You also can supply the special value browser, which causes different cached versions of the control to be created when the type and major version of the browser differs.)

For example, the User Control in Listing 23.24 uses a sliding expiration policy of one minute. When you specify a sliding expiration policy, a User Control is cached just as long as you continue to request the User Control within the specified interval of time.

Listing 23.24. SlidingUserCache.ascx

<%@ Control Language="VB" ClassName="SlidingUserCache" %> <%@ OutputCache Duration="10" VaryByParam="none" %> <script runat="server"> Sub Page_Load() CachePolicy.SetSlidingExpiration(true) CachePolicy.Duration = TimeSpan.FromMinutes(1) End Sub </script> User Control Time: <%= DateTime.Now.ToString("T") %>

The CD that accompanies this book includes a page named ShowSlidingUserCache.aspx, which contains the SlidingUserCache control. If you keep requesting this page, and do not let more than one minute pass between requests, then the User Control isn't dropped from the cache.

Creating a User Control Cache File Dependency

You can use the CacheControlPolicy.Dependency property to create a dependency between a cached User Control and a file (or set of files) on the file system. When the file is modified, the User Control is dropped from the cache automatically and reloaded with the next page request.

For example, the User Control in Listing 23.25 displays all the movies from the Movies.xml file in a GridView control. Notice that the User Control includes a Page_Load() handler that creates a dependency on the Movies.xml file.

Listing 23.25. MovieFileDependency.ascx

<%@ Control Language="VB" ClassName="MovieFileDependency" %> <%@ OutputCache Duration="9999" VaryByParam="none" %> <script runat="server"> Sub Page_Load() Dim depend As New CacheDependency(MapPath("~/Movies.xml")) Me.CachePolicy.Dependency = depend End Sub </script> User Control Time: <%= DateTime.Now.ToString("T") %> <hr /> <asp:GridView DataSource Runat="server" /> <asp:XmlDataSource DataFile="Movies.xml" Runat="server" />

The CD that accompanies this book includes a page named ShowMovieFileDependency, which displays the MovieFileDependency User Control (see Figure 23.9). If you open the page, then the User Control is automatically cached until you modify the Movies.xml file.

Figure 23.9. Displaying a User Control with a file dependency.

Caching Dynamically Loaded User Controls

You can load a User Control dynamically by using the Page.LoadControl() method. You can cache dynamically loaded User Controls in the same way that you can cache User Controls declared in a page. If a User Control includes an <%@ OutputCache %> directive, then the User Control will be cached regardless of whether the control was added to a page declaratively or programmatically.

However, you need to be aware that when a cached User Control is loaded dynamically, the ASP.NET Framework automatically wraps the User Control in an instance of the PartialCachingControl class. Therefore, you need to cast the control returned by the Page.LoadControl() method to an instance of the PartialCachingControl class.

For example, the page in Listing 23.26 dynamically adds the Movies User Control in its Page_Load() event handler. The Page_Load() method overrides the default cache duration specified in the User Control's <%@ OutputCache %> directive. The cache duration is changed to 15 seconds (see Figure 23.10).

Figure 23.10. Programmatically caching a User Control.

Listing 23.26. ShowDynamicUserControl.aspx

[View full width]

<%@ Page Language="VB" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <script runat="server"> Sub Page_Load() ' Load the control Dim cacheMe As PartialCachingControl = CType(Page.LoadControl("Movies.ascx"), PartialCachingControl) ' Change cache duration to 15 seconds cacheMe.CachePolicy.SetExpires(DateTime.Now.AddSeconds(15)) ' Add control to page PlaceHolder1.Controls.Add(cacheMe) ' Display control cache duration lblCacheDuration.Text = cacheMe.CachePolicy.Duration.ToString() End Sub </script> <html xmlns="http://www.w3.org/1999/xhtml" > <head runat="server"> <title>Show Dynamic User Control</title> </head> <body> <form runat="server"> <div> Cache Duration: <asp:Label Runat="server" /> <hr /> <asp:PlaceHolder Runat="server" /> </div> </form> </body> </html>

In Listing 23.26, the default cache duration is modified by modifying the PartialCachingControl's CachePolicy property. This property returns an instance of the same ControlCachePolicy class described in the two previous sections of this chapter.

You can refer to the User Control contained with an instance of the PartialCachingControl class by using the class's CachedControl property. Normally, this property returns the value Nothing (null) because when the User Control is cached, it is never actually created.

Категории