How to Use Caching

IOTA^_^    

Sams Teach Yourself ASP.NET in 21 Days, Second Edition
By Chris Payne
Table of Contents
Day 14.  Using ASP.NET's Improved Caching Capabilities


ASP.NET allows you to control output and data caching to suit your application's needs. Each method gives you slightly different mechanisms for caching, so it's important to understand all of them.

The next few sections will examine three different methods for caching data in ASP.NET. Each of these methods has advantages and disadvantages. Luckily, implementing them is a snap, so you'll be caching data in no time!

Caching Page Output

Enabling output caching is very simple. All you need is the OutputCache page directive:

 <%@ OutputCache Duration="time" Location="where" VaryByParam="how" %> 

This causes the output to be cached in the location specified by the where parameter for the number of seconds specified by the time parameter. The Location parameter can be set to any of the values in Table 14.1. (Note that Location is an optional parameter.) The VaryByParam attribute allows you to cache different versions of the page based on querystring values; we'll discuss VaryByParam a bit more once you've created your first cached page.

Table 14.1. Valid Location Parameter Values
Value Description
Any The output cache can be stored in any of the following locations. This is the default value.
Client The output cache will be located on the client.
Downstream The cache will be stored on a server "downstream" from the server that originally processed the request. This is used in the case of proxy servers.
None Output caching is not enabled for the requested page.
Server The cache is located on the server that processed the request.

When you're using output caching, all information is stored on the hard disk of the computer specified by Location instead of in memory. This has a greater negative impact on performance, but it also means that output caching provides a virtually free method of caching; it doesn't use up any valuable resources.

Let's take a look at an example. Listing 14.1 displays a simple welcome message and the current time.

Listing 14.1 Using the Output Cache
 1:    <%@ Page Language="C#" %> 2:    <%@ OutputCache Duration="30" VaryByParam="none" %> 3: 4:    <script runat="server"> 5:       void Page_Load(Object Sender, EventArgs e) { 6:          lblMessage.Text = "Welcome! The time is now " + DateTime.Now.ToString("T"); 7:       } 8:    </script> 9: 10:    <html><body> 11:       <font size="5">Using the Output Cache</font><hr> 12: 13:       <asp:Label  runat="server"/> 14:    </body></html> 

graphics/analysis_icon.gif

This looks like a normal ASP.NET page with an additional OutputCache directive. It causes the contents generated by the ASP.NET page to be cached for 30 seconds. After that, the cache will be invalidated (in other words, it will expire) and the next request will cause this page to be cached again. The VaryByParam attribute is set to none, which means that we're ignoring any querystring values (again, more on this in a moment). Figure 14.3 shows the output of this code.

Figure 14.3. The results of caching the current time.

graphics/14fig03.gif

Notice the time in the output. If you click Refresh, the page will keep displaying the same time. This is because the page is retrieved from the server cache, and the code inside the page isn't executed. If you revisit this page after 30 seconds, the time will be updated because the cache has expired.

The caching mechanism is a lot more powerful than this simple example shows. As mentioned earlier, the output cache can compare the querystrings supplied to a page to determine what to cache. Let's modify Listing 14.1 slightly to show this effect (see Listing 14.2).

Listing 14.2 Supporting Querystring Caching
 1:    <%@ Page Language="C#" %> 2:    <%@ OutputCache Duration="30" VaryByParam="true" %> 3: 4:    <script runat="server"> 5:       void Page_Load(Object Sender, EventArgs e) { 6:          lblMessage.Text = "Welcome " + Request.Params["id"] + "! The time is now " +  graphics/ccc.gifDateTime.Now.ToString("T"); 7:       } 8:    </script> 9: 10:    <html><body> 11:       <font size="5">Using the Output Cache</font><hr> 12: 13:       <asp:Label  runat="server"/> 14:    </body></html> 

graphics/analysis_icon.gif

Listing 14.2 added a method to retrieve a querystring parameter id on line 6. Notice also that the VaryByParam attribute is now set to true, instructing ASP. NET to take notice of the querystring. Save this page as listing1402.aspx and request it from the browser, giving your name to the page in the querystring (such as http://localhost/TYASPNET21Days/Day14/listing1402.aspx?id=Chris). You should now see a customized welcome message. Also note the time if you click the Refresh button, you should still see the same time.

Now modify the URL slightly: listing1402.aspx?id=Sammy. You should see a new welcome message, with a more current time. Refresh this page a few times to ensure the page is cached.

Finally, type in the original URL and request the page again: listing1402.aspx?id=Chris. You'll see the original welcome message, as well as the original time! You're not moving backward through time, but rather requesting a page that was already cached. Figure 14.4 shows the progression of three separate requests to the page.

Figure 14.4. ASP.NET reads the querystring parameters to determine the caching procedure.

graphics/14fig04.gif

Because you set VaryByParam to true, ASP.NET reads the querystring to determine if the requested page is in the cache. If the querystring is different, ASP.NET registers a cache miss, which means that it didn't find the page in cache. It then reexecutes the page and caches the new version. (Note that a cache miss is also generated when the cache expires and a request is made, causing the page to be executed again.)

Note

This means that requests with the same querystring parameters, but in a different order, will generate two different cached pages. For example:

 page.aspx?first=Chris&last=Payne 

This will generate a different cache than the following:

 page.aspx?last=Payne&first=Chris 


You can control this functionality by setting additional attributes (other than VaryByParam) in the <%@ OutputCache %> directive: VaryByHeader and VaryByCustom. VaryByHeader is used to cache pages based on the HTTP headers in the page. For example, you can use the following directive to vary caching by the language the user is using to browse the Web site:

 <%@ OutputCache Duration="60" VaryByParam="None" VaryByHeader="Accept-Language" %> 

We won't cover the various HTTP headers here, so let's move to the next varying mechanism.

VaryByCustom takes a comma-delimited list of variables that ASP.NET should use to distinguish cached items. For example, the following directive tells ASP.NET to cache page outputs as separate items only if the firstname querystring value differs. Any other querystring values will not cause new items to be cached:

 <%@ OutputCache Duration="600" VaryByParam="true" VaryByCustom="firstname" %> 

Add this directive to Listing 14.2 and request it from the browser using the following URL: http://localhost/tyaspnet21days/day14/listing1402.aspx?firstname=Chris&lastname=Payne. Note the time that's displayed. Now change the URL to read listing1402.aspx?firstname=Chris&lastname=Saravia and refresh the page. Note that the time doesn't change because the page with the firstname querystring value Chris has already been cached. The lastname querystring value is ignored. You can add the lastname parameter to the directive so the page is cached based on both querystring values:

 <%@ OutputCache Duration="600" VaryByParam="true" VaryByCustom="firstname,lastname" %> 

Note that if you leave the VaryByCustom parameter out, the default is to vary caching by all supplied parameters.

For example, this could be used on a Web site that sells concert tickets. First the user selects the state that the concert will take place in and the price she's willing to pay for tickets. The selected state and price are stored in the querystring. The next page pulls a list of concert events from a database, sorted by state, and caches the page output. The page should be cached based only on the selected state. The price that the user is willing to pay has no bearing on this page; it's only "tagging along" on the querystring. By setting VaryByCustom="state" and not price, you limit the number of pages that will be cached and limit the resources you're using.

You can also use VaryByCustom to vary caching by other variables, such as the browser version. For example, the following string will create a different set of cached pages for each different major browser version:

 <%@ OutputCache Duration="30" VaryByParam="None" VaryByCustom="majorversion" %> 
User Controls

Caching can be extended to user controls. For example, if you only wanted to cache part of a page a DataGrid, for instance you could wrap that part in a user control and set its cache options with the OutputCache directive. This is known as fragment caching.

Fragment caching is very useful when only parts of your page should be cached. For example, let's say you've created a simple user control that displays text boxes for the user to enter username and password information. This control doesn't need to be regenerated upon every request to the page because it never changes. Therefore, it can be cached. However, the page containing the user control may contain sensitive information, such as the time or personalized content. In that case, you wouldn't want to cache the entire page because the user will continue seeing old information. Only non-changing elements should be cached, such as the user control.

Listing 14.3 shows the definition for a simple user control. (See Day 6, "Learning More About Web Forms," for information on how to create user controls.)

Listing 14.3 A Cached User Control
 1:    <%@ Control Language="VB" %> 2:    <%@ OutputCache Duration="30" %> 3: 4:    <script runat="server"> 5:       sub Page_Load(Sender as Object, e as EventArgs) 6:          lblMessage.Text = "Control last viewed at " & _ 7:             DateTime.Now.ToString("T") 8:       end sub 9:    </script> 10: 11:    <font size="5">Using the Output Cache</font><hr> 12:    <asp:Label  runat="server"/> 

graphics/analysis_icon.gif

This control simply displays a label showing the last time that this control was accessed. Notice that the cache duration is set to 30 seconds on line 2. Save this control as control.ascx. Listing 14.4 shows an ASP.NET page using this control.

Listing 14.4 Implementing the User Control
 1:    <%@ Page Language="C#" %> 2:    <%@ Register TagPrefix="MyControl" TagName="View" src="/books/4/226/1/html/2/control.ascx" %> 3: 4:    <script runat="server"> 5:       void Page_Load(Object Sender, EventArgs e) { 6:          lblMessage.Text = "Page last viewed at " + DateTime.Now.ToString("T"); 7:       } 8:    </script> 9: 10:    <html><body> 11:       <MyControl:View runat="server" /><p> 12:       <asp:Label  runat="server"/> 13:    </body></html> 

graphics/analysis_icon.gif

Again, this page is very simple. The user control is registered on line 2. The Page Load event displays the time when the page is accessed. The user control is implemented on line 11. Note that Listing 14.4 doesn't contain the OutputCache directive. That's only contained in the user control.

When you request this page from the browser, you should see that the access times for the control and page are the same. Refresh the page, however, and you'll see the difference. The control is cached, so you still see an old version of it. However, the page isn't cached, so its access time keeps getting updated. Figure 14.5 shows the discrepancy in time after the page is refreshed.

Figure 14.5. Fragment caching in action.

graphics/14fig05.gif

Caution

Because the user control is cached, any attempts to modify it programmatically in the ASP.NET page will result in errors. For instance, trying to assign the user control an id value or giving it other properties will result in errors. Since the control is cached, its code isn't evaluated when requested and attempts to modify it will fail.

This may seem like a huge drawback to caching, but you should never experience problems if you use it correctly. Only the items that won't change should be cached. Typically, these items don't need to be manipulated in code at all. If you find that you do need to manipulate a cached control, perhaps it shouldn't have been cached in the first place.


This is a great way to cache only certain parts of pages. As you'll see in the next section, however, the Cache object provides an even more useful method.

Caching Objects

You can use the Cache class to add objects to the cache programmatically, which can also greatly improve performance. Imagine creating a large DataGrid object a process that takes time. You must connect to the data, retrieve it, and then create and populate the DataGrid. What if you could do all that just one time and cache the results? In fact, caching data returned from databases is one of the most common uses of the Cache class.

The Cache class provides three advantages over output caching. First, you can cache any object you want; you don't need to cache an entire page or user control. Rather, you can cache items with more granularity, such as data returned from a database or an individual server control. Second, the Cache class allows you to use sliding expiration. Finally, you can set dependencies on the items placed in the Cache class. This means that you can set cached data to be dependent on another item. A cached DataGrid can be dependent on the underlying data. When the data source changes, the cached DataGrid will be rendered invalid and created anew. See "Cache Dependencies" later today for more infor-mation.

Listing 14.5 shows an example of using the Cache class to cache data returned from a database. When this page is loaded for the first time, you retrieve the data from the database and bind it to a DataGrid. You'll also store it in cache so that the next time the page is requested, you can simply pull the data from the cache. This results in faster execution.

Listing 14.5 Caching Database Results with the Cache Class
 1:    <%@ Page Language="C#" %> 2:    <%@ Import Namespace="System.Data" %> 3:    <%@ Import Namespace="System.Data.OleDb" %> 4: 5:    <script runat="server"> 6:       void Page_Load(Object Sender, EventArgs e) { 7:          if (!Page.IsPostBack) { 8:             CreateData(); 9:          } 10:       } 11: 12:       void CreateData() { 13:          DataView source; 14: 15:          source = (DataView)Cache["DataSet"]; 16: 17:          if (source == null) { 18:             string strConnString = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source= C:\\ graphics/ccc.gifASPNET\\data\\banking.mdb"; 19: 20:             OleDbDataAdapter objCmd = new OleDbDataAdapter("select * from tblUsers",  graphics/ccc.gifstrConnString); 21: 22:             DataSet ds = new DataSet(); 23:             objCmd.Fill(ds, "tblUsers"); 24: 25:             source = new DataView(ds.Tables["tblUsers"]); 26:             Cache["DataSet"] = source; 27: 28:             lblMessage.Text = "Data set created explicitly"; 29:          } else { 30:             lblMessage.Text = "Data set retrieved from cache"; 31:          } 32: 33:          dgData.DataSource = source; 34:          dgData.DataBind(); 35:       } 36: 37:       void ExpireCache(Object Sender, EventArgs e) { 38:          Cache.Remove("DataSet"); 39:          CreateData(); 40:       } 41:    </script> 42: 43:    <html><body> 44:       <form runat="server"> 45:          <asp:Label  runat="server" 46:             maintainState=false/><p> 47:          <asp:Button  runat="server" 48:             text="Expire Cache" 49:             OnClick="ExpireCache"/><p> 50:          <asp:DataGrid  runat="server" 51:             BorderColor="black" 52:             GridLines="Vertical" 53:             cellpadding="4" 54:             cellspacing="0" 55:             width="450" 56:             Font-Name="Arial" 57:             Font-Size="8pt" 58:             HeaderStyle-BackColor="#cccc99" 59:             ItemStyle-BackColor="#ffffff" 60:             AlternatingItemStyle-Backcolor="#cccccc" /> 61:       </form> 62:    </body></html> 

graphics/analysis_icon.gif

The Page Load event on lines 6 10 is fairly standard. The CreateData method on line 12 is where the real action takes place.

You want to store a DataView of the data returned from the database for easier access later. Therefore, on line 13 you create a new DataView. On line 15, you try to retrieve this DataView from the Cache object. If there is no DataView with the specified name in cache (as it should be upon first viewing), your variable source is set to null (or nothing in VB.NET). Note that the items stored in cache are of the Object data type, which is why you must cast your variables accordingly as on line 15.

The if statement on line 17 checks this condition. If source is equal to null, meaning there was nothing in cache, you want to retrieve the data from the database. Lines 18 24 perform this retrieval you should be fairly familiar with this process by now. On line 25, you set your source variable to a DataView on the DataSet, and you store it in cache on line 26. The result is that this particular view of your returned data is now in cache, which in turn means that the returned data is in cache. Lines 28 and 30 simply display a text message telling you where the data came from.

In order to test your application, you also want to be able to clear the cache manually. The ExpireCache method on line 37 does this. Whenever the user clicks the button on line 47, this event handler executes. The Remove method of the Cache object removes the specified item from cache. In this case, you want to remove your DataView. Finally, you call your CreateData method again to display the DataGrid once more.

Note

The Remove method returns an object of the type that you want to delete. This allows you to perform additional operations on the object if necessary. For example, you could replace line 40 with the following:

 DataView dv = (DataView)Cache.Remove("DataView"); 

This allows you to perform any final operations on the object before it's finally expunged from cache, such as retrieving the information one last time.

If you specify the name of an object that doesn't exist in cache, this method returns null.


After you call the Remove method on your DataView, you have to retrieve the data from the database once again. This means the if statement on line 17 tries to execute the code block within. Figure 14.6 shows an example output from this listing upon first viewing, and Figure 14.7 shows the output after Refresh has been clicked.

Figure 14.6. Caching data returned from a database.

graphics/14fig06.gif

Figure 14.7. Upon subsequent views, the DataSet is retrieved from cache.

graphics/14fig07.gif

The first time you view this page, you should see "Data set created explicitly." Clicking Refresh will cause "Data set retrieved from cache" to appear. Finally, clicking the Expire Cache button will cause the original message to reappear. Depending on your computer setup, you may see an obvious change in performance when retrieving the information from the cache. Even though it may not seem like much of a difference here, imagine thousands of users trying to access the same page at once. The benefits of caching increase exponentially with the load.

Note

Note that if you click the Refresh button after clicking the Expire Cache button, your browser will ask if you want to resubmit the form data. Answering Yes will resubmit the button click, causing the cache to be expunged again and leaving unwary users wondering why they're getting a "Data set created explicitly" message rather than "Data set retrieved from cache."

If you want to refresh the page after clicking the Expire Cache button, you'll have to retype the URL into the browser. Or you can simply add a hyperlink to the page, setting it to the same URL as this page. Essentially, this will cause a refresh.


There are three different ways to place items in the cache. The first one was examined in the previous listing. The next two ways are with the Add and Insert methods. The two methods are similar, but the Insert method provides you with more choices. Let's look at the syntax for the Add method first:

 Cache.Add(key, value, dependencies, absoluteExpiration, _    slidingExpiration, priority,  _    onRemoveCallBack) 

There are a lot of parameters with this method, so let's examine each one in detail. The first two, key and value, are what you used in previous listings. key is the name of the object you want to store in cache, and value is the actual item or object to store. dependencies will be covered in a moment. absoluteExpiration is the absolute date that you want the cache to expire, such as "June 6, 2001."

slidingExpiration is similar to absoluteExpiration, but it refreshes the expiration date each time the page is requested. For example, if you set the expiration date to five hours from now with absoluteExpiration, the cache will expire in five hours no matter what. With slidingExpiration, the cache will expire five hours after the last request. This means that if someone requests the page four hours from now, the cache will expire five hours after that, which is nine hours from now, and so on. Thus, the expiration date slides as requests come in. You specify this sliding time with a TimeSpan object.

The priority parameter determines how the cache will throw away the object in certain situations, such as when memory is low. Generally, items with a higher priority will remain in the cache longer than those with a lower priority. This is roughly analogous to the cost of keeping the item in memory. If an item has a higher cost to create in terms of time or system resources, you should keep it in cache longer, and vice versa.

Finally, onRemoveCallBack is the method that should be called when this item is removed from cache.

Let's add your DataView to the cache with the Add method, replacing line 28 in Listing 14.5:

 Cache.Add("DataSet", source, null, DateTime.Now. AddMinutes(1), TimeSpan.Zero,  graphics/ccc.gifCacheItemPriority.High, _ ic:ccc]OnRemove); 

graphics/analysis_icon.gif

Here you add the source variable, which contains the DataView you want to store. You set the dependency to null for now, set the expiration date to one minute from now, and set sliding expiration to 0, which effectively turns sliding off. You use the High and Slow values of the CacheItemPriority object to set the priority parameter (see Table 14.2 for descriptions of these values). Finally, the last parameter specifies the delegate that will be executed when this item is removed from cache. (Note that you could set this value to null if you didn't want to handle the event.) If you specify a method name that doesn't exist, you'll receive an error.

Delegates are a special type of object that we haven't yet discussed thoroughly, and we're only going to briefly cover them here. A delegate is essentially a pointer to a method. Events use delegates to determine what event handler should be executed when the event is raised. So OnRemove in this case needs to point to a method to handle the removal of a cache item. In C#, this is a three-step process.

First, you need to create the delegate itself, just like you would any other variable. Immediately after line 5 of Listing 14.5 (before the Page_Load method), insert the following code:

 private static CacheItemRemovedCallback OnRemove; 

We now have a delegate named OnRemove that is of type CacheItemRemovedCallback. This type dictates how our event handler will look creating this method is the next step. The following code snippet shows the HandleRemove method:

 void HandleRemove(String key, Object value, CacheItemRemovedReason reason)    [code] } 

This method receives the key and object that was removed, as well as the reason for its removal. The reason parameter can be one of the following:

  • DependencyChanged An object that this item depended on (such as a database) has changed.

  • Expired This object is past its expiration date.

  • Removed You used the Remove method.

  • Underused The system removed it from cache because the computer was running low on memory.

The third step is to assign your event handler to your delegate. In the Page_Load method, add the following code:

 OnRemove = new CacheItemRemovedCallback(this.HandleRemove); 

Now, whenever an item is removed from cache, the HandleRemove method will be executed. In VB.NET, this process is a bit easier; you don't need to worry about delegates because VB.NET handles them for you. All you need to do is change the Cache.Add method to read as follows:

 Cache.Add("DataSet", source, nothing, DateTime.Now.AddMinutes(1), TimeSpan.Zero,  graphics/ccc.gifCacheItemPriority.High, addressof HandleRemove) 

The addressof operator provides the same function as a delegate.

Table 14.2 lists the different values for the CacheItemPriorities parameter.

Table 14.2. CacheItemPriorities Values
Priority Values Description
AboveNormal These items are less likely to be removed from cache than items with a Normal priority.
BelowNormal More likely to be removed than items with a Normal priority.
Default This value evaluates to Normal.
High Items with this value are least likely to be removed from cache.
Low The most likely to be removed from cache.
Normal The base level of priority.
NotRemovable These items will not be removed from cache.

Note

Note that the descriptions in Table 14.2 only apply when the cache automatically removes items due to memory limit restrictions, and not when manually removing items.


The Insert method provides the exact same syntax but allows some parameters to be optional. You can call the Insert method in any of the following ways:

 Cache.Insert(key, value) Cache.Insert(key, value, dependencies) Cache.Insert(key, value, dependencies, absoluteExpiration, _    slidingExpiration) Cache.Insert(key, value, dependencies, absoluteExpiration, _    slidingExpiration, priority, _    onRemoveCallBack) 

Caution

If you insert or add an item with the same name as an object already in cache, the existing item will be removed in favor of the new one.


With this mechanism, you can cache nearly any part of a page, from DataViews to DataGrids to user controls.

Cache Dependencies

The caching mechanism of ASP.NET allows you to specify that items stored in cache via the Cache class are dependent on other items. For example, suppose you've created a cached DataView object from the data in an XML file. If the XML file has changed, you want the DataView to change as well. Figure 14.8 illustrates the connection between cache and external sources.

Figure 14.8. Dependencies allow the cache to depend on another source.

graphics/14fig08.gif

Without the dependency shown in Figure 14.7, the cache would have no idea that the data has changed and users would keep receiving old, out-of-date data.

Cache dependencies can be created on files, directories, or other items in cache. Let's modify Listing 14.5 a bit more to retrieve the data from the XML file you created on Day 11, "Using XML in ASP.NET," and to add a dependency to your object (see Listing 14.6). This time we'll use VB.NET instead of C#.

Listing 14.6 Using Cache Dependencies
 1:    <%@ Page Language="VB" %> 2:    <%@ Import Namespace="System.Data" %> 3:    <%@ Import Namespace="System.Data.OleDb" %> 4:    <%@ Import Namespace="System.IO" %> 5: 6:    <script language="VB" runat="server"> 7:       sub Page_Load(Sender as Object, e as EventArgs) 8:          if not Page.IsPostBack then 9:             CreateData() 10:          end if 11:       end sub 12: 13:       sub CreateData() 14:          dim source as DataView 15:          dim objFs as FileStream 16:          dim objReader as StreamReader 17:          dim ds as new DataSet() 18: 19:          source = Cache("DataView") 20: 21:          if source is nothing then 22:             objFs = New FileStream(Server.MapPath _ 23:                ("../day11/books.xml"), FileMode.Open, _ 24:                FileAccess.Read) 25:             objReader = New StreamReader(objFs) 26:             ds.ReadXml(objReader) 27:             objFs.Close() 28: 29:             dim objDepend as new _ 30:                CacheDependency(Server.MapPath ("../day11/books.xml")) 31: 32:             source = new DataView(ds.Tables(0)) 33:             Cache.Insert("DataView", source, objDepend, DateTime.Now.AddMinutes(1),  graphics/ccc.gifTimeSpan.Zero) 34: 35:             lblMessage.Text = "Data set created explicitly" 36:          else 37:             lblMessage.Text = "Data set retrieved from cache" 38:          end if 39: 40:          dgData.DataSource = source 41:          dgData.DataBind() 42:       end sub 43: 44:    </script> 45: 46:    <html><body> 47:       <form runat="server"> 48:          <asp:Label  runat="server" 49:             maintainState=false/><p> 50:          <asp:DataGrid  runat="server" 51:             BorderColor="black" 52:             GridLines="Vertical" 53:             cellpadding="4" 54:             cellspacing="0" 55:             width="450" 56:             Font-Name="Arial" 57:             Font-Size="8pt" 58:             HeaderStyle-BackColor="#cccc99" 59:             ItemStyle-BackColor="#ffffff" 60:             AlternatingItemStyle-Backcolor="#cccccc" /> 61:       </form> 62:    </body></html> 

graphics/analysis_icon.gif

The HTML portion of the listing hasn't changed (with the exception of removing the Expire Cache button). Most of this listing is the same as Listing 14.5, but let's look at the differences.

On lines 22 27, you retrieve data from an XML file instead of your database. However, you place the data in a DataSet as before. (Don't forget to include the System.IO namespace.) On line 29, you create a new CacheDependency object in the books.xml XML file you created on Day 11. Finally, you display a message telling the user where the data came from, and you bind the data to the DataGrid.

This page should work as before. The first time you view it, the DataView will be created explicitly. Subsequent requests will retrieve the data from cache. Now open the XML file in Notepad or a similar word processor, and add a new record. Request the page again and you'll notice that the data is created explicitly once again. Without the dependency, ASP.NET would have no way to know the data has changed and would keep delivering the old data from cache. Figure 14.9 shows the output after you've added some records to the XML file.

Figure 14.9. Updating a dependency causes the cache to expire and be re-created.

graphics/14fig09.gif

Using the HttpCachePolicy Class

The previous sections showed the basics of how to use page caching. This may be enough to suit your needs, but ASP.NET provides another class that gives you greater control over the caching mechanism the HttpCachePolicy class. You can access this class through the HttpResponse object's Cache member (Response.Cache). It contains advanced methods for manipulating how the cache is handled. The HttpCachePolicy object uses HTTP headers to control caching, thereby controlling page output caching. The difference between the HttpCachePolicy and page output caching is that the former allows much greater control over the caching mechanism.

Manipulating the Cache

The Response.Cache object offers a few different methods to manipulate the expiration dates. Using these methods allows you to replace the OutputCache directive.

The first method, SetExpires, sets the absolute expiration time for the cache. For example, the following tells the cache to expire 30 seconds from now:

 Response.Cache.SetExpires(DateTime.Now.AddSeconds(30)) 

The following code snippet tells the cache to expire at 3 p.m. today, local time:

 Response.Cache.SetExpires(DateTime.Parse("3:00:00PM")) 

Another way to set the absolute expiration time is with the SetMaxAge method. This tells the cache the maximum age the cache can be before it must be invalidated. For example, the following line tells the cache that it must expire after 0 hours, 30 minutes, and 0 seconds:

 Response.Cache.SetMaxAge(new TimeSpan(0,30,0)) 

These two methods are very similar. Use the SetExpires method if you know the exact time you want the cache to expire (even if it's relative to the current time). Use the SetMaxAge method if you don't know or don't care the exact time the cache expires, but you do know that it must expire after a certain amount of time. Also note that SetExpires takes a DateTime parameter, while SetMaxAge takes a TimeSpan parameter.

The Response.Cache object also allows you to set sliding expiration times (see "Caching Objects" earlier today). To enable sliding expiration, you simply set the method to true:

 Response.Cache.SetSlidingExpiration(true) 

The cache will expire on a sliding time span that's updated with each new request.

Note

Note that if you set sliding expiration to true, you must set the expiration date to some time relative to the current time. For instance, the following code snippet would not result in a sliding expiration date:

 Response.Cache.SetExpires(DateTime.Parse("11/21/01 3:00:00PM")) Response.Cache.SetSlidingExpiration(true) 

ASP.NET cannot figure out how to slide the expiration from an exact time. However, the following snippet will work:

 Response.Cache.SetExpires(DateTime.Now.AddSeconds(30)) Response.Cache.SetSlidingExpiration(true) 

The sliding expiration will be 30 seconds from the last request.


You can also control if and where the cache is stored. The SetCacheability method allows you to specify who is allowed to cache your pages. Table 14.3 lists the types of cacheability.

Table 14.3. Types of Cacheability
Type Description
NoCache Allows no one to cache items. The client must revalidate the items with the server.
Private Specifies that clients can cache the response, but not shared (proxy) servers. This is the default value.
Public Specifies that both clients and proxy servers can cache the response.
Server Specifies that the response is cached only at the server that generated the response.

Setting this property is useful when you know where your response is going and how you want it to be treated. For example, if your response is going to a network via a proxy server, you can prevent the proxy server from caching your response so the clients aren't dealing with a "middleman" cache:

 Response.Cache.SetCacheability(HttpCacheability.Private) 

In a similar vein, the SetNoServerCaching method stops the server from caching a document altogether. All future requests to that document will be processed fully not retrieved from a cache. For example:

 Response.Cache.SetNoServerCaching() 

Note

You may be wondering where you can place all these commands to the Response.Cache object. You have a lot of freedom any method of your code declaration block will do. However, usually you'll want to place such commands at the beginning of your Page Load event handler to ensure that all of your output is handled correctly.


Varying Cache Mechanisms

Recall what you learned earlier today about caching and its examinations of querystrings using the VaryByParam attribute? The default behavior is for ASP.NET to store each different variation of the querystring as a separate item in cache. You can control this behavior very easily, however.

The VaryByParams property (note the "s" on the end) of the Response.Cache object tells how to cache objects based on parameters specified by the page, either through querystrings or through form variables. It works exactly the same as with page output caching. Listing 14.7 shows an example.

Listing 14.7 Varying Caching by Parameters
 1:    <%@ Page Language="VB" %> 2:    <%@ OutputCache Duration="60" %> 3: 4:    <script runat="server"> 5:       sub Page_Load(Sender as Object, e as EventArgs) 6:          lblMessage.Text = "Welcome " & Request.Params("first") & _ 7:             "! The time is now " & DateTime.Now.ToString("T") 8: 9:          lblMessage.Text += "<br>" & Response.Cache. _ 10:             VaryByParams.Item("first").ToString 11:          lblMessage.Text += "<br>" & Response.Cache. _ 12:             VaryByParams.Item("last").ToString 13:       end sub 14:    </script> 15: 16:    <html><body> 17:       <font size="5">Using the Output Cache</font><hr> 18:       <form runat="server"> 19:          <asp:Label  runat="server"/><p> 20:       </form> 21:    </body></html> 

graphics/analysis_icon.gif

Listing 14.7 is similar to Listing 14.2, with the addition of the VaryByParams properties on lines 9 12. When you request this page, you'll supply two parameters, first and last, which represent the first and last names of the user. On first viewing, the page looks like Figure 14.10.

Figure 14.10. Showing the VaryByParams items.

graphics/14fig10.gif

The items first and last in the VaryByParams property (which evaluate to the first and last querystring parameters) both evaluate to false, as shown in Figure 14.9. This means that a separate version of this document is available in cache for each different parameter. The version varies depending on the parameter's value.

This is the default behavior, and you witnessed it earlier today in "Caching Page Output." Specifying different first and last values will result in a different page being stored in cache. Specifying the same parameters will retrieve the item already stored in cache.

This is where it gets interesting. Let's add the following line to Listing 14.7 as the first line in the Page Load event:

 Response.Cache.VaryByParams.Item("first") = true 

Now try changing the querystring parameters. Changing first will result in a new page being cached. Changing last, although it keeps first the same, won't result in a new cached page. It will return an old one, as evidenced by the time stamp. Figure 14.11 shows this difference.

Figure 14.11. Varying the last parameter does not cache a new page.

graphics/14fig11.gif

By changing the VaryByParams value to true, you're telling ASP.NET to cache a new page only when the first parameter changes. Likewise, setting the following will cause ASP.NET to cache pages only when the last value changes:

 Response.Cache.VaryByParams.Item("last") = true 

This is a very useful property. Imagine, for instance, that you're caching pages for a site that determines a person's tax status by state. You pass the state abbreviation and her name in the querystring. However, all that matters for tax purposes is the state she lives in, so you don't need to cache by her name. You could set the following to only update the cache when a new state is requested:

 Response.Cache.VaryByParams.Item("state") = true 

This can provide a large performance benefit if used correctly.

There's also a method to vary caching by HTTP headers called VaryByHeaders, which again works similarly to the attribute discussed in the section on page output caching. This book hasn't discussed the various HTTP headers in depth, so we'll leave it up to you to explore this topic. The VaryByCustom attribute is missing from the Response.Cache object, but the SetVaryByCustom method in its place works similarly. (See the .NET Framework SDK documentation for more information.)


    IOTA^_^    
    Top


    Sams Teach Yourself ASP. NET in 21 Days
    Sams Teach Yourself ASP.NET in 21 Days (2nd Edition)
    ISBN: 0672324458
    EAN: 2147483647
    Year: 2003
    Pages: 307
    Authors: Chris Payne

    flylib.com © 2008-2017.
    If you may any questions please contact us: flylib@qtcs.net