Visual C#. NET 2003 Unleashed

Developers often need to temporarily put data (temporary data) in a storage medium (for example, a database or some location in memory) for quick access. The storage of this temporary data is called a cache.

A cache provides a number of possibilities. For example, imagine that you're working with a database in which data will be changed very rarely. When a user requests data, you will retrieve the same data each time from the database. Each time, the server will process the same operation to the retrieve the data and the performance of the application will be decreased. The best solution in such a situation is to store retrieved data in storage (at least for a short period of time). After storing the data, you can read it directly from the cache. In the cache, you could store database data as well as whole HTML pagesit depends on the logic of the application being implemented. Experience in web application development will teach you that developers often store data retrieved from a database in static variables (static hash tables, arrays, and so on), but do not often store HTML pages on the server side with help of ASP.NET's cache toolkit. But ASP.NET's mechanism of caching is very powerful and helpful (especially in small web applications).

All cached data is stored in memory so that it can be retrieved and processed significantly faster than if it were stored on magnetic media such as a hard disk. If a developer uses the cache correctly, he can greatly increase the performance of his application.

ASP.NET presents simple and very powerful possibilities of working with data caching. It enables you to cache whole HTML pages (or just parts of them) and other data in the global object HttpCache.

Introduction to ASP.NET Caching

The code in Listing 24.1 is a simple example that shows how to use a cache in ASP.NET.

Listing 24.1. Caching HTML Pages by Using the OutputCache Directive

<%@ Page language="c#" Codebehind="Main.aspx.cs" AutoEventWireup="false" Inherits="OutputCacheSample.Main" %> <%@ OutputCache Duration="60" VaryByParam="none"%> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" > <html> <head> <title>OutputCache using Sample</title> <meta name="GENERATOR" Content="Microsoft Visual Studio .NET 7.1"> <meta name="CODE_LANGUAGE" Content="C#"> <meta name=vs_defaultClientScript content="JavaScript"> <meta name=vs_ targetSchema content="http://schemas.microsoft.com/intellisense/ie5"> </head> <body MS_POSITIONING="GridLayout"> <table cellpadding="0" cellspacing="0" border="0"> <tr> <td> Page's invocation time: </td> <td> <%=DateTime.Now.ToString("F")%> </td> </tr> </table> </body> </html>

This example simply outputs the server's time on a web page. When the page first appears, it is cached because of the OutputCache directive in the second line of Listing 24.1. The page will be stored in the cache for 60 seconds. If a user requests this page, his browser will show the cached page with the old time. After the cache has been cleared, the user can view the updated page.

We have two ways to cache HTML pages:

  • Using the OutputCache directive

  • Using the HttpCachePolicy class

OutputCache and HttpCachePolicy are very similar, but using OutputCache is more user friendly. The following section discusses how to use the OutputCache directive in detail.

OutputCache Directive

Listing 24.1 showed how to use the OutputCache directive to quickly retrieve HTML pages from the cache. That example is very simple because it uses just one attribute from the available set of predefined parameters of the OutputCache directive. It uses the parameter Duration, which declares the time (in seconds) that pages should be stored in the cache. The example also declares that the page will have just one cached version, independent of the set of parameters of this one.

The OutputCache directive has the following attributes:

<%@ OutputCache Duration="value" Location="value" VaryByParam="value" VaryByHeader="value" VaryByCustom="value" %>

The following list describes each attribute in more detail:

  • Duration Sets the period of time (in seconds) during which pages should be saved in the cache (in Listing 24.1. it is set to 60 seconds). If a user requests a cached page before 60 seconds have expired, the page will not be processed at the server side, but will be read from the cache. This attribute is mandatory. If it's omitted, ASP.NET will throw an exception.

  • Location Indicates the location where cached pages should be stored. With help of this attribute, we can indicate whether a page should be cached in the server's RAM or in the client's browser. Location can be set to the following values:

    • Any The page will be cached anywhere: on the server, on the client's browser, and so on. This value is the default.

    • Client The page will be cached just on the client side (if it supports caching).

  • Downstream The page will be cached on the sending server.

  • None Caching is disabled.

  • Server The page will be cached on the server that processes the user's request.

To use the Location attribute in OutputCache, you should change the second line in Listing 24.1 to the following:

<%@ OutputCache Duration="60" VaryByParam="none" Location="Server"%>

In this case, the page will be cached for 60 seconds only on the server side.

TIP

Note that different values of the Location attribute will cause different behavior by the application. You should use it very carefully, taking into account the settings of the server and client browsers.

  • VaryByParam Enables you to have several instances of one page in the cache. A page could be cached in different ways, depending on the set of parameters that are passed over the query string (GET parameters) and the form's parameters (POST parameters). Caching could also depend on HTTP headers. If this attribute is set to none, the page will be cached only once (without taking into account parameters that are passed over the GET and POST mechanisms). But web pages are often built dynamically with different sets of parameters, so in that case you should use the VaryByParam attribute to define pages that should be cached (for example, you could cache a page by the value of some GET parameter).

    Imagine that you need to create a forum. The probability of anyone changing already-posted messages in the forum is very small, so you could cache those messages. For example, the name of the page that shows the text of some message is message.aspx and it contains the MID (message ID) parameter in the request string. For caching each message in the forum, you should use the following format of the OutputCache directive:

    <%@ OutputCache Duration="60" VaryByParam="MID" Location="server"%>

    In this case, the server will cache the message.aspx page and take into account the value of the MID parameter. The message.aspx page will have several cached copies on the server side (the number of copies is equal to the number of messages that have been reviewed by different users during the previous 60 seconds). If several users want to review the same message in the forum, the page with that message will be generated just one time (meaning that there will be just one request in the database, too). Other users will see a cached copy of message.aspx with a particular value of the MID parameter. This approach will result in greatly improved performance by the application.

    You can also set several parameters for filtering cached pages inside VaryByParam attribute by using a semicolon. For example:

    <%@ OutputCache Duration="60" VaryByParam="FID;MID" Location="server"%>

    In this case, messages will be put in the cache by forum ID (FID) and message ID (MID). You can also use an asterisk in the VaryByParam attribute's value. In such a case, the page will be cached according to values of all parameters that are passed to it.

  • VaryByHeader With the help of this attribute, we can manage HTML page caching according to the values of the HTTP headers. This attribute could be set in one of following values: Accept, Accept-Charset, Accept-Encoding, Accept-Language, Authorization, Content-Encoding, Expect, From, Host, If-Match, If-Modified-Since, If-None-Match, If-Range, If-Unmodified-Since, Max-Forwards, Proxy-Authorization, Range, Referer, TE, and User-Agent.

    You can use a semicolon to define several values of HTTP headers in the VaryByHeader attribute.

    <%@ OutputCache Duration="60" VaryByHeader=" If-None-Match; Range " VaryByParam="none"%>

    The preceding example declares that the page caching depends on the values of If-None-Match and Range HTTP headers. It means that every new combination of these headers will cause a new copy of the page to be written in the cache.

  • VaryByCustom Use of this attribute enables you to manage page caching according to parameters, which are defined by the developer. The default value of this attribute is Browser. If you use this attribute with the Browse value, the page will be cached according to the client's browser type and the elder numbers of its version identifier.

    <%@ OutputCache Duration="60" VaryByCustom="Browse" VaryByParm="none"%>

    As mentioned earlier, developers can create custom values for the VaryByCustom attribute. Developers can do so by redefining the HttpApplication.GetVaryByCustomString method. That method could be redefined in the global.asax file.

Using HttpCachePolicy

As mentioned earlier, ASP.NET provides two ways to cache HTML pages. The first approachthe OutputCache directivewas discussed earlier. So, the following section covers the second approach: the HttpCachePolicy class.

To be more precise, the OutputCache directive is just an intuitive interface of the HttpCachePolicy class. But OutputCache gives more limited possibilities than HttpCachePolicy. For example, you aren't allowed to set an absolute expiration time for storing an object in the cache with the help of OutputCache, but HttpCachePolicy allows you to do so. You could retrieve access to that object via the Response.Cache object represented in ASP.NET.

Listing 24.2 is an example that shows the simplest way of using HttpCachePolicy. (The ASPX representation of this example is the same as in Listing 24.1, but without the use of the OutputCache directive.)

Listing 24.2. Caching HTML Pages by Using the HttpCachePolicy Class

using System; using System.Collections; using System.ComponentModel; using System.Data; using System.Drawing; using System.Web; using System.Web.SessionState; using System.Web.UI; using System.Web.UI.WebControls; using System.Web.UI.HtmlControls; namespace HttpCachePolicySample { public class Main : System.Web.UI.Page { private void Page_Load(object sender, System.EventArgs e) { Response.Cache.SetCacheability(HttpCacheability.Server); Response.Cache.SetExpires(DateTime.Now.AddSeconds(60)); } #region Web Form Designer generated code override protected void OnInit(EventArgs e) { InitializeComponent(); base.OnInit(e); } private void InitializeComponent() { this.Load += new System.EventHandler(this.Page_Load); } #endregion } }

This example caches the page by using two methods of the HttpCachePolicy class: SetExpires and SetCacheability. In this case, the page will be cached on the server side for 60 seconds. You could also use the HttpCacheability.Public enumeration member for caching page anywhere (doing so is the same as using the Any value for the OutputCache.Location attribute).

TIP

Note that this article doesn't describe all the members and methods of the HttpCachePolicy class in detail. You can find all necessary information about them on any website related to ASP.NET technology and on Microsoft's MSDN site. In this chapter, we review only the most demonstrative methods of the class.

The SetExpires method helps to set an absolute expiration time for a page in the cache. The example in Listing 24.2 shows how to use SetExpires in tandem with the DateTime class. You are also allowed to use it in the following way:

Response.Cache.SetExpires(DateTime.Now.AddSeconds(60)); Response.Cache.SetExpires(DateTime.Parse("04:00"));

In this case, the page will be deleted from the cache at 4 o'clock local time.

Both methods that have been discussed enable you to set an absolute expiration time. But there is one more ASP.NET method that enables you to set the caching type to a sliding mode:

Response.Cache.SetSlidingExpiration( true );

When you use absolute caching, the page is stored in the cache until a particular amount of time expires, without taking into account the quantity of requests that have been performed to this page. When you use a sliding expiration, the page will be deleted from cache only when the page has not been requested by any user for a certain length of time (in the sample case, it's 60 seconds). If a user requests the page during that length of time, the page will again be stored in the cache for an interval equal to the previous one.

Using the Cache Object

There is another class in ASP.NET that enables you to cache data. This class is called Cache, and you get access to it via the Cache property of the current context of the web application (for example, Context.Cache).

This object enables you to both store data and to retrieve stored data. It also supports the following:

  • Memory management It automatically removes an object from the cache to prevent memory overflow.

  • Deleting object after some event An object could be put in the cache with some rules that dictate when it will be removed automatically.

  • Callback Cache supports the ability to notify your code when an item is removed from the cache.

Listing 24.3 shows how to use the Cache object. This example puts some data into the cache and shows that data in the browser. It also checks the source from which the data has been retrieved.

Listing 24.3. Using the Cache Object (Code-Behind)

using System; using System.Collections; using System.ComponentModel; using System.Data; using System.Drawing; using System.Web; using System.Web.SessionState; using System.Web.UI; using System.Web.UI.WebControls; using System.Web.UI.HtmlControls; namespace CacheSample { public class Main : System.Web.UI.Page { protected HtmlTableCell DataCell; protected HtmlTableCell SourceCell; private void Page_Load(object sender, System.EventArgs e) { String data = "Some data that should be cached."; String source = ""; if (Cache[SOME_DATA_KEY] != null) { data = Cache[SOME_DATA_KEY].ToString(); source = "Cache"; } else { Cache[SOME_DATA_KEY] = data; source = "Not Cache"; } DataCell.InnerText = data; SourceCell.InnerText = source; } private const string SOME_DATA_KEY = "SOME_DATA_KEY"; #region Web Form Designer generated code override protected void OnInit(EventArgs e) { InitializeComponent(); base.OnInit(e); } private void InitializeComponent() { this.Load += new System.EventHandler(this.Page_Load); } #endregion } }

Listing 24.4 shows the HTML version of this example.

Listing 24.4. Using Cache Object (HTML Representation)

<%@ Page language="c#" Codebehind="Main.aspx.cs" AutoEventWireup="false" Inherits="CacheSample.Main" %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" > <html> <head> <title>Cache using Sample</title> <meta name="GENERATOR" Content="Microsoft Visual Studio .NET 7.1"> <meta name="CODE_LANGUAGE" Content="C#"> <meta name=vs_defaultClientScript content="JavaScript"> <meta name=vs_targetSchema content="http://schemas.microsoft.com/intellisense/ie5"> </head> <body MS_POSITIONING="GridLayout"> <table width="100%" cellpadding="0" cellspasing="0" border="0"> <tr> <td width="20%">Data: </td> <td runat="server" ></td> </tr> <tr> <td>Source: </td> <td runat="server" ></td> </tr> </table> </body> </html>

After the first request to the page shown in Listings 24.3 and 24.4, you will see that the source of the data is Not Cache. All other requests (until the page is deleted from the cache) will return that data retrieved from the cache.

TIP

Note that it is mandatory to check whether an object is located in the cache; otherwise, ASP.NET will throw the exception NullReferenceException when you attempt to reference an item in the cache that does not exist.

The preceding example used an implicit approach to putting objects in the cache. You also could use the following method invocation:

Cache.Insert("SOME_DATA_KEY", data);

The Insert method also has three additional overloads that are discussed in the upcoming sections. Assume that you've read data from a file and put it in the cache. There is some chance that file could be changed, so data in the cache also should be updated or deleted. The Cache object enables you to trace all changes in a file with data by using an additional parameter (in Cache.Insert method) with type System.Web.Caching.CacheDependency. To use this feature, you create an instance of the CacheDependency class and pass it to the Cache.Insert method:

System.Web.Caching.CacheDependency dependency = new System.Web.Caching.Dependency(Server.MapPath(someFileName)); Cache.Insert(SOME_DATA_KEY, data, dependency);

Before returning an object, Cache traces changes in the file that the object depends on. If the file has changed, the object is removed from Cache; otherwise, it is returned to Cache's user.

The System.Web.Caching.CacheDependency class has overloads for the constructor, which enables you to trace the changes in the following:

  • In a file

  • In a folder

  • In a set of files

  • In a set of folders

  • In other objects in Cache

TIP

Unfortunately, due to the transient nature of instances of Page classes, there is no built-in way in ASP.NET to create a dependency that removes an item from the cache in response to changes in a DataSet.

Another feature is that Cache enables you to set absolute and sliding expiration times for objects that should be inserted in it. (The differences between absolute and sliding time were discussed earlier in this chapter.)

Cache.Insert("SOME_DATA_KEY", data, null, DateTime.Now.AddMinutes(5), System.Web.Caching.Cache.NoSlidingExpiration); Cache.Insert("SOME_DATA_KEY", data, null, System.Web.Caching.Cache.NoAbsoluteExpiration, TimeSpan.FromMinutes(2));

The first line shows how to use absolute object expiration time. The second line presents a sliding expiration time.

TIP

Notice that the System.Web.Caching.Cache.NoSlidingExpiration constant is of type System.TimeSpan, and System.Web.Caching.Cache.NoAbsoluteExpiration is of type System.DateTime.

As was mentioned earlier, Cache can manage memory and automatically remove objects from memory if an overflow occurs. An algorithm for this mechanism is encapsulated in the Cache object, and you cannot review or change it, but you're allowed to exert some influence on the process. You can do this with the help of the priority parameter in the Cache.Insert method. This one is of type System.Web.Caching.CacheItemPriority.

An object of lower priority will be deleted from the cache faster than objects with higher priority. You can also prohibit deletion of an object from the cache.

To set an object's priority in the cache, you use the enumeration System.Web.Caching.CacheItemPriority, which has the following values: Low, BelowNormal, Default, Normal, AboveNormal, High, and NotRemovable. An object of CacheItemPriority.NotRemovable priority will not be deleted during cache clearing.

The last parameter of the Cache.Insert method to be discussed is the onRemoveCallback parameter of type System.Web.Caching.CacheItemRemovedCallback. This parameter takes as its value a delegate that will be invoked during the deletion of a particular object from the cache. This allows your code to notify you about the deletion of an object from the cache.

Cache.Insert("SOME_DATA_KEY", data, null, DateTime.Now.AddMinutes(5), System.Web.Caching.Cache.NoSlidingExpiration, System.Web.Caching.CacheItemPriority.Low, new System.Web.Caching.CacheItemRemovedCallback(SomeDelegate)); ... ... ... private void SomeDelegate(string key, object val, System.Web.Caching.CacheItemRemovedReason reason) { ... }

Cache.Remove("SOME_DATA_KEY");

TIP

You could remove an object from the cache manually by using the Cache.Remove method, which requires just one parameter: key (the name of object).

The final capability of the Cache object discussed in this chapter is that Cache implements the IEnumerable interface. That is why you can very simply and quickly gain access to all objects located in the Cache object.

foreach(DictionaryEntry cacheEntry in Cache) { //access to key - cacheEntry.Key.ToString(); //access to value - Cache[cacheEntry.Key.ToString()].ToString(); }

    Категории