ptg 1354 CHAPTER 29 Caching Application Pages and Data <%= DateTime.Now.ToString(“T”) %> <hr /> <asp:GridView id=”grdMovies” DataSourceID=”srcMovies” Runat=”server” /> <asp:SqlDataSource id=”srcMovies” ConnectionString=”<%$ ConnectionStrings:Movies %>” SelectCommand=”SELECT Title, Director FROM Movies” Runat=”server” /> <br /><br /> <a href=”AddMovie.aspx”>Add Movie</a> </div> </form> </body> </html> The page in Listing 29.12 contains a DetailsView control that enables you to add a new movie. When you insert a new movie into the database, the Response.RemoveOutputCacheItem() method is called to remove the MovieList.aspx page from the cache. Because this method accepts only a “virtual absolute” path, the Page.ResolveUrl() method converts the tilde into the application root path. LISTING 29.12 AddMovie.aspx <%@ Page Language=”C#” %> <!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN” “http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”> <script runat=”server”> protected void dtlMovie_ItemInserted(object sender, ➥DetailsViewInsertedEventArgs e) { HttpResponse.RemoveOutputCacheItem(Page.ResolveUrl(“~/MovieList.aspx”)); Response.Redirect(“~/MovieList.aspx”); } </script> <html xmlns=”http://www.w3.org/1999/xhtml” > <head id=”Head1” runat=”server”> <title>Add Movie</title> From the Library of Wow! eBook ptg 1355 Using Page Output Caching 29 </head> <body> <form id=”form1” runat=”server”> <div> <h1>Add Movie</h1> <asp:DetailsView id=”dtlMovie” DefaultMode=”Insert” DataSourceID=”srcMovies” AutoGenerateRows=”false” AutoGenerateInsertButton=”true” Runat=”server” OnItemInserted=”dtlMovie_ItemInserted”> <Fields> <asp:BoundField DataField=”Title” HeaderText=”Title:” /> <asp:BoundField DataField=”Director” HeaderText=”Director:” /> </Fields> </asp:DetailsView> <asp:SqlDataSource id=”srcMovies” ConnectionString=”<%$ ConnectionStrings:Movies %>” InsertCommand=”INSERT Movies (Title, Director) VALUES (@Title, @Director)” Runat=”server” /> </div> </form> </body> </html> The Response.RemoveOutputCacheItem() method enables you to remove only one page from the cache at a time. If you need to remove multiple pages, you can create a key dependency, which enables you to create a dependency between one item in the cache and another item. When the second item is removed from the cache, the first item is removed automatically. For example, the page in Listing 29.13 also displays a list of movies. However, the page is cached with a dependency on an item in the cache named Movies. From the Library of Wow! eBook ptg 1356 CHAPTER 29 Caching Application Pages and Data LISTING 29.13 MovieListKeyDependency.aspx <%@ Page Language=”C#” %> <%@ OutputCache Duration=”3600” 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”> protected void Page_Load(object sender, EventArgs e) { Cache.Insert(“Movies”, DateTime.Now); Response.AddCacheItemDependency(“Movies”); } </script> <html xmlns=”http://www.w3.org/1999/xhtml” > <head id=”Head1” runat=”server”> <title>Movie List Key Dependency</title> </head> <body> <form id=”form1” runat=”server”> <div> <%= DateTime.Now.ToString(“T”) %> <hr /> <asp:GridView id=”grdMovies” DataSourceID=”srcMovies” Runat=”server” /> <asp:SqlDataSource id=”srcMovies” ConnectionString=”<%$ ConnectionStrings:Movies %>” SelectCommand=”SELECT Title, Director FROM Movies” Runat=”server” /> <br /><br /> <a href=”AddMovieKeyDependency.aspx”>Add Movie</a> </div> </form> </body> </html> From the Library of Wow! eBook ptg 1357 Using Page Output Caching 29 The page in Listing 29.14 enables you to add a new movie to the Movies database table. When the new movie is inserted, the Movies item is removed, and any pages dependent on the Movies item are dropped from the cache automatically. LISTING 29.14 AddMovieKeyDependency.aspx <%@ Page Language=”C#” %> <!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN” “http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”> <script runat=”server”> protected void dtlMovie_ItemInserted(object sender, ➥DetailsViewInsertedEventArgs e) { Cache.Remove(“Movies”); Response.Redirect(“~/MovieListKeyDependency.aspx”); } </script> <html xmlns=”http://www.w3.org/1999/xhtml” > <head id=”Head1” runat=”server”> <title>Add Movie Key Dependency</title> </head> <body> <form id=”form1” runat=”server”> <div> <h1>Add Movie</h1> <asp:DetailsView id=”dtlMovie” DefaultMode=”Insert” DataSourceID=”srcMovies” AutoGenerateRows=”false” AutoGenerateInsertButton=”true” Runat=”server” OnItemInserted=”dtlMovie_ItemInserted”> <Fields> <asp:BoundField DataField=”Title” HeaderText=”Title:” /> <asp:BoundField DataField=”Director” HeaderText=”Director:” /> </Fields> </asp:DetailsView> From the Library of Wow! eBook ptg 1358 CHAPTER 29 Caching Application Pages and Data <asp:SqlDataSource id=”srcMovies” ConnectionString=”<%$ ConnectionStrings:Movies %>” InsertCommand=”INSERT Movies (Title, Director) VALUES (@Title, @Director)” Runat=”server” /> </div> </form> </body> </html> Manipulating the Page Output Cache Programmatically If you need more control over how ASP.NET Framework caches pages, then you can work directly with the HttpCachePolicy class. This class is exposed by the Response.Cache property. The HttpCachePolicy class includes properties and methods that enable you to perform programmatically all the tasks that you can perform with the <%@ OutputCache %> direc- tive. You also can use the methods of this class to manipulate the HTTP cache headers that are sent to proxy servers and browsers. This class supports the following properties: . VaryByHeaders—Gets the list of headers that are used to vary cache output. . VaryByParams—Gets the list of query string and form parameters used to vary cache output. The HttpCachePolicy class also supports the following methods: . AddValidationCallback—Enables you to create a method called automatically before a page is retrieved from the cache. . AppendCacheExtension—Enables you to add custom text to the Cache-Control HTTP header. . SetAllowResponseInBrowserHistory—Enables you to prevent a page from appearing in the browser history cache. . SetCacheability—Enables you to set the Cache-Control header and the server cache. . SetETag—Enables you to set the ETag HTTP header. . SetETagFromFileDependencies—Enables you to set the ETag HTTP header from the time stamps of all files on which the page is dependent. . SetExpires—Enables you to set the Expires HTTP header. From the Library of Wow! eBook ptg 1359 Using Page Output Caching 29 . SetLastModified—Enables you to set the Last-Modified HTTP header. . SetLastModifiedFromFileDependencies—Enables you to set the Last-Modified HTTP header from the time stamps of all files on which the page is dependent. . SetMaxAge—Enables you to set the Cache-Control:max-age HTTP header. . SetNoServerCaching—Enables you to disable web server caching. . SetNoStore—Enables you to send a Cache-Control:no-store HTTP header. . SetNoTransform—Enables you to send a Cache-Control:no-transform HTTP header. . SetOmitVaryStar—Enables you to not send the vary:* HTTP header. . SetProxyMaxAge—Enables you to set the Cache-Control:s-maxage HTTP header. . SetRevalidation—Enables you to set the Cache-Control HTTP header to either must-revalidation or proxy-revalidate. . SetSlidingExpiration—Enables you to set a sliding expiration policy. . SetValidUntilExpires—Enables you to prevent a page from expiring from the web server cache when a browser sends a Cache-Control header. . SetVaryByCustom—Enables you to set the string passed to the GetVaryByCustomString() method in the Global.asax file. For example, the page in Listing 29.15 programmatically places a page in the output cache. The page is cached on the browser, proxy servers, and web server for 15 seconds. LISTING 29.15 ProgramOutputCache.aspx <%@ Page Language=”C#” %> <!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN” “http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”> <script runat=”server”> void Page_Load() { Response.Cache.SetCacheability(HttpCacheability.Public); Response.Cache.SetExpires(DateTime.Now.AddSeconds(15)); Response.Cache.SetMaxAge(TimeSpan.FromSeconds(15)); Response.Cache.SetValidUntilExpires(true); Response.Cache.SetLastModified(DateTime.Now); Response.Cache.SetOmitVaryStar(true); } </script> <html xmlns=”http://www.w3.org/1999/xhtml” > <head id=”Head1” runat=”server”> <title>Program OutputCache</title> From the Library of Wow! eBook ptg 1360 CHAPTER 29 Caching Application Pages and Data </head> <body> <form id=”form1” runat=”server”> <div> <%= DateTime.Now.ToString(“T”) %> <br /><br /> <a href=”ProgramOutputCache.aspx”>Request this Page</a> </div> </form> </body> </html> Clearly, it is more difficult to enable Page Output Caching programmatically than declara- tively. You need to call many methods to cache a page in the same way as you can with a single <%@ OutputCache %> directive. However, programmatically manipulating the cache provides you with fine-grained control over the HTTP headers sent to proxy servers and browsers. Creating Page Output Cache Profiles Instead of configuring Page Output Caching for each page in an application, you can configure Page Output Caching in a web configuration file and apply the settings to multiple pages. You can create a Cache Profile. Creating Cache Profiles makes your website easier to manage. For example, the web configuration file in Listing 29.16 contains the definition for a Cache Profile named Cache1Hour that caches a page for one hour. LISTING 29.16 Web.Config <?xml version=”1.0”?> <configuration> <system.web> <caching> <outputCacheSettings> <outputCacheProfiles> <add name=”Cache1Hour” duration=”3600” varyByParam=”none” /> </outputCacheProfiles> </outputCacheSettings> </caching> </system.web> </configuration> From the Library of Wow! eBook ptg 1361 Using Partial Page Caching 29 The page in Listing 29.17 uses the Cache1Hour profile. This profile is set with the <%@ OutputCache %> directive’s CacheProfile attribute. LISTING 29.17 OutputCacheProfile.aspx <%@ Page Language=”C#” %> <%@ OutputCache CacheProfile=”Cache1Hour” %> <!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN” “http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”> <html xmlns=”http://www.w3.org/1999/xhtml” > <head id=”Head1” runat=”server”> <title>Output Cache Profile</title> </head> <body> <form id=”form1” runat=”server”> <div> <%= DateTime.Now.ToString(“T”) %> </div> </form> </body> </html> You can set the same caching properties in a Cache Profile as you can set in an individual page’s <%@ OutputCache %> directive. For example, you can set varyByParam, varyByControl, varyByHeader, and even varyByCustom attributes in a Cache Profile. NOTE ASP.NET 4 introduces a new feature called Extensible Output Caching, which enables you to configure a custom output-cache provider that utilizes whatever storage medium you choose—local disks, cloud-based storage, and so on. To learn more about develop- ing a custom output-cache provider, check out the MSDN website (http://msdn. microsoft.com). Using Partial Page Caching 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. From the Library of Wow! eBook ptg 1362 CHAPTER 29 Caching Application Pages and Data 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 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, you can use the ASP.NET Substitution control. For example, the page in Listing 29.18 uses the Substitution control to display the current time on a page that has been output cached (see Figure 29.6). FIGURE 29.6 Using the Substitution control. From the Library of Wow! eBook ptg 1363 Using Partial Page Caching 29 LISTING 29.18 SubstitutionControl.aspx <%@ Page Language=”C#” %> <%@ 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 static string GetTime(HttpContext context) { return DateTime.Now.ToString(“T”); } </script> <html xmlns=”http://www.w3.org/1999/xhtml” > <head id=”Head1” runat=”server”> <title>Substitution Control</title> </head> <body> <form id=”form1” runat=”server”> <div> The cached time is: <%= DateTime.Now.ToString(“T”) %> <hr /> The substitution time is: <asp:Substitution id=”Substitution1” MethodName=”GetTime” Runat=”server” /> </div> </form> </body> </html> In Listing 29.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 prop- erty 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 29.19. From the Library of Wow! eBook . and any pages dependent on the Movies item are dropped from the cache automatically. LISTING 29. 14 AddMovieKeyDependency.aspx <%@ Page Language=”C#” %> <!DOCTYPE html PUBLIC -/ /W3C//DTD. application root path. LISTING 29.12 AddMovie.aspx <%@ Page Language=”C#” %> <!DOCTYPE html PUBLIC -/ /W3C//DTD XHTML 1.0 Transitional//EN” “http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”>. </html> Manipulating the Page Output Cache Programmatically If you need more control over how ASP. NET Framework caches pages, then you can work directly with the HttpCachePolicy class. This class is exposed