Output Caching

The easiest way to implement caching in your ASP.NET web applications is through directives at either the page or the user control level. Unfortunately, this method is also the most limiting and frustrating to work with.

@OutputCache Directive

The basis for output caching at both the page and the user control level is the @OutputCache directive. Although you can use many different possible attributes (a few of which you’ll see in a bit), two are mandatory: Duration and VaryByParam.VaryByControl can be substituted for VaryByParam in user controls, however.

Duration Attribute

The Duration attribute is fairly straightforward, but you must realize that the number it accepts is the number of “seconds” that you want your content to be cached for. Accidentally specifying a value of 10, for instance, would cache your data only for 10 seconds instead of 10 minutes (which is what you might expect).

If you forget to specify the Duration attribute, you’ll get an error like this:

								Parser Error Message: The directive is missing a 'duration' attribute.

VaryByParam Attribute

Misuse of the VaryByParam attribute can lead to some strange behavior by your ASP.NET page. This is the second mandatory attribute of the @OutputCache directive. Failure to include it will yield an error message like this:

								Parser Error Message: The directive is missing a ‘VaryByParam’ attribute,, which
 should be set to "none", "*", or a list of name/value pairs. 

If you specify none as a value for this attribute, you must be aware that the same cached page will be served up for all requests for that page. For instance, if you intended to cache product information pages on your web application, and set the VaryByParam attribute to none, then the first product that was requested would be served up for all subsequent requests, regardless of any changes that were made to the query string. The following two page requests would both return the data for the product with an ID = 10 :

http://www.yourdomain.com/product.aspx?ID=10

http://www.yourdomain.com/product.aspx?ID=20

You might choose to fix this anomaly by specifying a value of * for the VaryByParam attribute. Although this might appear to solve the problem, what happens if you are passing information on the query string that is personal to the current user—say, perhaps a user ID? You would then get a cached version for each user that browsed each product. Take the following two page requests:

http://www.yourdomain.com/product.aspx?ID=10&user=1234

http://www.yourdomain.com/product.aspx?ID=10&user=5678

You would want both of these requests to be served up from the same item in cache. This is where you would want to specify exactly which QueryString parameters you want the cache to key off. In this case, it would be the ID parameter only.

It is useful to note that, in the scenario just discussed, you would likely want to place your @OutputCache directive in a user control that contains your product-rendering code so that the user QueryString parameter could be used by other sections of the page to personalize the output. Then, only the product information itself would be served up from cache.

If you need to specify more than one QueryString parameter as a caching key, make sure that you use a semicolon as the delimiter instead of a comma. The value ID, user would be interpreted as one QueryString parameter, thereby causing the wrong caching behavior. The tricky part is that no errors are generated when you make this mistake. Instead, use ID; user.

VaryByCustom Attribute

When using the VaryByCustom attribute to customize the key used to store your page or user control in cache, you must remember to override the GetVaryByCustomString method in your web application’s global.asax file. No errors will be generated if you forget to do this, but your custom parameter will not take part in the cache key.

VaryByHeader Attribute

The most common error that you will run into with the VaryByHeader attribute occurs if you try to use it in the @OutputCache directive of a user control. This is not allowed and will generate an error like this:

								Parser Error Message: The 'VaryByHeader' attribute is not supported by the 'OutputCache'
 directive..

Use the VaryByHeader attribute only at the page level.

Manipulation of Cached User Controls

If you implement the @OutputCache directive in a user control, you cannot manipulate its properties externally. Dynamically assigning the properties of a user control in one of the page’s events (such as Page_Load) works only if a copy does not already exist in the fragment cache. Subsequent attempts to dynamically modify one of the properties of a user control that has output caching enabled will generate an error like this:

							Exception Details: System.NullReferenceException: Value null was found where an instance
 of an object was required.

As you can see, this error message isn’t very intuitive. To be safe, always set the properties of a cached user control in the Page_Load event inside the user control itself. The exception to this is when you want to use declarative attributes; specifying attributes in the user control tag itself is permitted. An example might look like this:

<chapter12:MyControl UserName="Jon" runat="server" //> 

Validation Callbacks

Remember the issue mentioned at the beginning of the chapter, pertaining to data being trapped in the output cache? Here is where we’ll put that issue to rest. Here you’ll wire up a validation callback so that each time the page is requested from the cache, you’ll get a chance to establish whether the cached item is still good. You’ll do this by checking for the existence of a password QueryString parameter. Take a look at the code in Listings 12.1 and 12.2, which demonstrate the technique.

Listing 12.1. Invalidating the Output Cache Through a Validation Callback (C#)
<%@ Page Language="C#" %> 
<%@ OutputCache Duration="10000" VaryByParam="none" %> 

<script language="C#" runat="server"> 
      protected void MyHttpCacheValidateHandler(HttpContext context, 
            object data, ref HttpValidationStatus validationStatus) 
      {
            //initialize the password variable 
            string password = ""; 

            //set the password variable if the user specified the 
            //"password" QueryString parameter 
            if(context.Request.QueryString["password"]!=null) 
            {
                  password = (string)context.Request.QueryString["password"]; 
            } 

            //if a password was specified, then determine if it is 
            //correct. If it is, then evict the page from the cache. 
            //If the password is incorrect or wasn't 
            //specified, then validate the cached page 
            switch(password) 
            {
                  case "mypassword": 
                        validationStatus = HttpValidationStatus.Invalid; 
                          break; 
                    default: 
                          validationStatus = HttpValidationStatus.Valid; 
                          break; 
             } 
       } 

       protected void Page_Load(Object sender, EventArgs e) 
       {
              //create an instance of the HttpCacheValidateHandler 
              //delegate so that we can wire up the validation callback 
              HttpCacheValidateHandler myHandler = 
                    new HttpCacheValidateHandler(MyHttpCacheValidateHandler); 

              //wire up the callback 
              Response.Cache.AddValidationCallback(myHandler, null); 

              //by displaying the current date and time on the screen, 
              //we can see when the cache is being invalidated 
              label1. Text = DateTime.Now. ToString(); 
       } 
</script> 

<html> 
<head> 
<title>Validation Callback</title> 
</head> 

<body> 
<asp:label id="label1" runat="server" /> 
</body> 
</html> 

Listing 12.2. Invalidating the Output Cache Through a Validation Callback (Visual Basic .NET)
<%@ Page Language="VB" %> 
<%@ OutputCache Duration="10000" VaryByParam="none" %> 

<script language="VB" runat="server"> 
      Protected Sub MyHttpCacheValidateHandler( _ 
            context As HttpContext, data As Object, _ 
            ByRef validationStatus As HttpValidationStatus) 

            'initialize the password variable 
            Dim password As String = "" 

            'set the password variable if the user specified the 
            '"password" QueryString parameter 
            If context.Request.QueryString("password") <> Nothing Then 
               password = CStr(context.Request.QueryString("password")) 
            End If 

            'if a password was specified, then determine if it is 
            'correct. If it is, then evict the page from the cache. 
            'If the password is incorrect or wasn't 
            'specified, then validate the cached page 
            Select Case password 
                  Case "mypassword" 
                       validationStatus = HttpValidationStatus.Invalid 
                  Case Else 
                       validationStatus = HttpValidationStatus.Valid 
            End Select 
      End Sub 

     Protected Sub Page_Load(sender As Object, e As EventArgs) 
           'create an instance of the HttpCacheValidateHandler 
           'delegate so that we can wire up the validation callback 
           Dim myHandler As HttpCacheValidateHandler = _ 
           New HttpCacheValidateHandler( _ 
           AddressOf MyHttpCacheValidateHandler) 

           'wire up the callback 
           Response.Cache.AddValidationCallback(myHandler, Nothing) 

           'by displaying the current date and time on the screen, 
           'we can see when the cache is being invalidated 
           label1. Text = DateTime.Now. ToString() 
      End Sub 
</script> 

<html> 
<head> 
<title>Validation Callback</title> 
</head> 

<body> 
<asp:label id="label1" runat="server" /> 
</body> 
</html> 

In Listings 12.1 and 12.2, you can see that we start off by specifying the @OutputCache directive with a duration of 10,000 seconds. This is plenty of time to demonstrate the validation callback. Also notice the fact that the VaryByParam attribute is set to none. Be sure not to specify a * for this attribute—if you do, you will never be able to reset the correct output cache. When the cache is invalidated, it immediately stores a new copy. With the * value for VaryByParam, the password QueryString parameter would become part of the new cache key. This is not the desired outcome. You can set the VaryByParam attribute to specific QueryString parameters. Just don’t include the password parameter as one of them.

Below the @OutputCache directive, you define a handler for the validation callback. In it, you check for the presence of the password QueryString parameter. If it exists, then you assign it to the password variable. If the password is correct, you set an Invalid status value to the validationStatus variable. Otherwise, you set a Valid status value.

Notice the use of a switch statement (Case statement, in Visual Basic .NET) instead of an if statement. As originally stated in Chapter 4,“code Structure that Eases Debugging,” this enhances the readability of the code and enables you to add more options if needed at a later time.

Next, you define your Page_Load event. You create an instance of the HttpCacheValidateHandler delegate, using the address of the handler function just defined. You can then wire up the callback through the AddValidationCallback method of the Cache property of the Response object.

To visually see when the cache is being invalidated, you assign the current date and time to a label server control on the page. To see the validation callback in action, you can navigate to the page in your browser. The first time you hit the page, it is stored in cache. This is evident when you refresh the page and the same date and time appear on the page. Now add the password=mypassword QueryString parameter to the URL and hit the Enter key. You can see that the cache is refreshed with a new version of the page because the date and time change. If you now remove the password QueryString parameter and hit the Enter key again, you’ll see that the newly cached page is fetched and displayed.

By using this technique, you can force a reset of the output cache, eliminating the problem of invalid debugging data (or any other incorrect data, for that matter) being trapped in cache.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset