You are on page 1of 5

.Caching with ASP.

NET
ASP.NET supports three types of caching for Web-based applications:
• Page Level Caching (called Output Caching)
• Page Fragment Caching (often called Partial-Page Output Caching)
• Programmatic or Data Caching

Output Caching
Page level, or output caching, caches the HTML output of dynamic requests to ASP.NET Web
pages. The way ASP.NET implements this (roughly) is through an Output Cache engine. Each time
an incoming ASP.NET page request comes in; this engine checks to see if the page being
requested has a cached output entry. If it does, this cached HTML is sent as a response;
otherwise, the page is dynamically rendered, its output is stored in the Output Cache engine.
Output Caching is particularly useful when you have very static pages. It is easy to implement. By
simply using the @OuputCache page directive, ASP.NET Web pages can take advantage of this
powerful technique. The syntax looks like this:

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

The Duration parameter specifies how long, in seconds, the HTML output of the Web page should
be held in the cache. When the duration expires, the cache becomes invalid and, with the next
visit, the cached content is flushed, the ASP.NET Web page's HTML dynamically generated, and
the cache repopulated with this HTML. The VaryByParam parameter is used to indicate whether
any GET (QueryString) or POST (via a form submit with method="POST") parameters should be
used in varying what gets cached. In other words, multiple versions of a page can be cached if
the output used to generate the page is different for different values passed in via either a GET or
POST.
The VaryByParam is a useful setting that can be used to cache different "views" of a dynamic
page whose content is generated by GET or POST values. For example, you may have an ASP.NET
Web page that reads in a Part number from the QueryString and displays information about a
particular widget whose part number matches the QueryString Part number. Imagine for a
moment that Output Caching ignored the QueryString parameters altogether (which you can do
by setting VaryByParam="none"). If the first user visited the page with QueryString
/ProductInfo.aspx?PartNo=4, she would see information out widget #4. The HTML for this page
would be cached. The next user now visits and wished to see information on widget #8, a la
/ProductInfo.aspx?PartNo=8. If VaryByParam is set to VaryByParam="none", the Output Caching
engine will assume that the requests to the two pages are synonymous, and return the cached
HTML for widget #4 to the person wishing to see widget #8! To solve for this problem, you can
specify that the Output Caching engine should vary its caches based on the PartNo parameter by
either specifying it explicitly, like VaryByParam="PartNo", or by saying to vary on all GET/POST
parameters, like: VaryByParam="*".

The full spec for the @OutputCache lines is:


<%@ OutputCache Duration="#ofseconds" Location="Any ¦ Client ¦ Downstream ¦
Server ¦ None" VaryByControl="controlname" VaryByCustom="browser ¦
customstring" VaryByHeader="headers" VaryByParam="parametername" %>

Duration is a count in seconds to cache.

Location allows the caching to occur on the server, on the client, on a proxy server in between.
The default is Any. If you always want server caching (which seems to me to be the most useful
choice), change the line to read:
<%@ OutputCache Duration="3600" Location="Server" VaryByParam="none"%>

VaryByControl is only used by user controls, not on standard web pages.


VaryByCustom="browser" keeps a different copy of the output for each browser name and
major version information. So if you have cloaking by browser version going on (which is easy to
implement in .NET), then each separate page will get delivered.

VaryByCustom="customstring" Allows you to specify a string that will be passed to your code.
To make this useful, you must then override the GetVaryByCustomString method in the
Global.asax file. For example, place this line in your ASPX file:
<%@ OutputCache Duration="3600" Location="Server" VaryByCustom="Referer"
VaryByParam="none"%>

Then in your Global.asax file add the following code:


public override String GetVaryByCustomString(System.Web.HttpContext hcContext, String
strCustom)
{
switch (strCustom)
{
case "Referer":
Uri uriReferrer = hcContext.Request.UrlReferrer;
String strRet;
if (uriReferrer != null)
strRet = uriReferrer.Host;
else
strRet = null;
return strRet;
default:
return base.GetVaryByCustomString(hcContext, strCustom);
}
}

VaryByHeader allows you to cache based off of some field in the HTTP header sent by the client.
The classic example is based off the Accept-Language header line.

VaryByParam allows you to cache different versions based off of querystring or post field
parameters. So http://www.domain.com/foo.aspx?bar=baz would be cached separately from
http://www.domain.com/foo.aspx?bar=bletch

Partial-Page Output Caching


More often than not, it is impractical to cache entire pages. For example, you may have some
content on your page that is fairly static, such as a listing of current inventory, but you may have
other information, such as the user's shopping cart, or the current stock price of the company,
that you wish to not be cached at all. Since Output Caching caches the HTML of the entire
ASP.NET Web page, clearly Output Caching cannot be used for these scenarios: enter Partial-Page
Output Caching.
Partial-Page Output Caching, or page fragment caching, allows specific regions of pages to be
cached. ASP.NET provides a way to take advantage of this powerful technique, requiring that the
part(s) of the page you wish to have cached appear in a User Control. One way to specify that the
contents of a User Control should be cached is to supply an OutputCache directive at the top of
the User Control. That's it! The content inside the User Control will now be cached for the
specified period, while the ASP.NET Web page that contains the User Control will continue to serve
dynamic content. (Note that for this you should not place an OutputCache directive in the
ASP.NET Web page that contains the User Control - just inside of the User Control.)

Data Caching
In simple terms data caching is storing data in memory for quick access. Typically information
that is costly to obtain (in terms of performance) is stored in the cache. One of the more common
items stored in a cache in a Web application environment is commonly displayed database values;
by caching such information, rather than relying on repeated database calls, the demand on the
Web server and database server's system resources are decreased and the Web application's
scalability increased. As Microsoft eloquently puts it, "Caching is a technique widely used in
computing to increase performance by keeping frequently accessed or expensive data in memory.
In the context of a Web application, caching is used to retain pages or data across HTTP requests
and reuse them without the expense of recreating them."

Using Data Caching


The .NET data caching API is comprised of the two classes in the System.Web.Caching
namespace. The first class, Cache, is the class we'll be using to add and remove items from the
data cache. The second class, CacheDependency, is used when assigning a cache dependency to
an item in the data cache. To add an item to the cache you can simply do:

Cache["key"] = value; // In C#

The above code adds the item value to the data cache with the key “key”. The key is used to
reference the item at some later point. That is, in another ASP.NET Web page we can extract the
value inserted above by using:

value = Cache("key")
- or -
value = Cache.Get("key")

To explicitly remove an item from the data cache you can use the Remove method, specifying the
key of the cache item you want removed:

Cache.Remove("key")

Inserting an Item into the Cache


Inserting an item into the data cache is as simple as saying Cache["key"] = value. Items can also
be inserted into the cache using the Insert method, which allows for more powerful semantics on
how the item in the cache should be handled. Specifically, the Insert method has four overloaded
forms, as shown below:
1. Insert(key as String, value as Object) - Inserts the object value into the cache, giving the
item the key name key. This is semantically equivalent to using Cache("key") = value.
2. Insert(key as String, value as Object, dependencies as CacheDependency) - Inserts the
object value into the cache with key name key and dependencies specified by the
dependencies parameter. We'll discuss cache dependencies shortly.
3. Insert(key as String, value as Object, dependencies as CacheDependency,
absoluteExpiration as DateTime, slidingExpiration as TimeSpan) - Inserts the object value
into the cache with key name key, dependencies dependencies, and (time-based)
expiration policies. Expiration policies, as we'll discuss soon, specify when the item should
be evicted from the cache.
4. Insert(key as String, value as Object, dependencies as CacheDependency,
absoluteExpiration as DateTime, slidingExpiration as TimeSpan, priority as
CacheItemPriority, onRemoveCallBack as CacheItemRemovedCallback) - Inserts the object
value into the cache with key name key, dependencies dependencies, (time-based)
expiration policies, a cache priority, and a callback delegate. The priority specifies how
important it is for the cache item to remain in the cache. That is, items with a lower
priority will be evicted from the cache before items with a higher priority. The callback
delegate provides a means for you to create your own function that is automatically called
when the item is evicted from the cache.
The above list of the various forms of the Insert method may look quite daunting. Most often
you'll likely use either form 1, 2, or 3. Let's take a moment to discuss the CacheDependency and
absolute and sliding time parameters.
Recall that information stored in the data cache is being stored on the Web server's memory. In a
perfect world, when an item is added to the cache using Insert(key, value) or Cache("key") =
value, the item will remain in the cache forever. Unfortunately this is not plausible in the real
world. If the computer the Web server runs on is rebooted, or shuts off, for example, the cache
will be lost. Even if the Web server machine is running, you may lose items from the cache.
To see why, imagine that your Web server has allocated one MB of memory for storing items in
the data cache. Now, imaging that you've added a number of items to the data cache such that
you've used exactly 1 MB of memory. Great. Now, what happens when you add another item to
the data cache? In order for the new item to "fit," the data cache needs to make room for it by
removing an existing item. The existing item that is chosen to be removed is said to be evicted.
There may be times when you don't want an item to exist in the cache indefinitely. For example,
say that you were displaying an XML file in an ASP.NET DataGrid. Rather than load the XML file
into a DataSet and bind the DataSet to the DataGrid each page view, you may opt to cache the
DataSet in the data cache. This option would work great until the XML file was altered; at that
point, if you were still displaying the cached DataSet the user would be seeing stale information.
To overcome this problem, you can add the DataSet to the data cache, but specify that the XML
file it represents is a cache dependency. By setting this file as a cache dependency, when the file
changes the DataSet will be automatically evicted from the cache. That means the next time the
DataSet is attempted to be read from the cache, it will not be found (since it has been evicted)
and will be recreated by repopulating the DataSet from the XML file. This is desired since the XML
file has changed since the DataSet was last cached. In order to insert an item with a cache
dependency, you can do:
Cache.Insert("key", myDataSet, New CacheDependency(Server.MapPath("data.xml")))

If you wish to have the cache item evicted from the cache in an absolute time, say, five minutes
from when it was inserted into the cache, you can use the third form of the Insert method, whose
fourth parameter expects a DateTime value specifying the absolute time. The following code
illustrates how to add an item to the cache that will expire five minutes from when it was added
and has no cache dependencies:

Cache.Insert("key", value, Nothing, DateTime.Now.AddMinutes(5), TimeSpan.Zero)

In C# you would use null instead of Nothing to signify that you do not want a cache dependency.
Note that since we do not want to specify sliding time expiration, we set the last parameter to
TimeSpan.Zero. Whereas an absolute time specifies that the item should be evicted from the
cache at a specific time, the sliding time eviction parameter specifies that the cache item should
be evicted if it is not referenced in a certain timespan. That is, if we set the timespan parameter
to, say, TimeSpan.FromSeconds(30), the cache item will be evicted if it is not referenced within
30 seconds. If it is referenced within 30 seconds, it will be evicted if it's not referenced in another
30 seconds from when it was last referenced, and so on. An example of this would be:

Cache.Insert("key", value, Nothing, DateTime.Now, TimeSpan.FromSeconds(30),


TimeSpan.Zero)

Note that when using the sliding time expiration parameter, the absolute expiration parameter
value does not matter. That is, it is automatically set to DateTime.Now and has the sliding time
added to it to determine the absolute time the cache item should expire. Of course, if the item is
referenced within that time period, the calculation is redone and the absolute expiration time is
reset.
Before we move on to some examples, let's take a quick look at the CacheItemRemovedCallback
delegate. Recall that you can set this in the fourth overloaded form of the Insert method. The
CacheItemRemovedCallback specifies a function that is called when the item has been evicted
from the cache. To use the CacheItemRemovedCallback you need to first create a function that
has the definition:

Sub CallbackFunction(String, Object, CacheItemRemovedReason)

The CacheItemRemovedReason is an enumeration that explains why the item was removed from
the cache. Its entries include:
1. DependencyChanged - the item was removed because its cache dependency was changed.
2. Expired - the item was removed because it expired (either by absolute or sliding time
expiration).
3. Removed - the item was explicitly removed via the Remove method.
4. Underused - the item was evicted by the cache because the system needed to free up
memory.
To add a CacheItemRemovedCallback function to an added cache item you will need to create the
appropriate function and a delegate variable that is wired up to the function, as shown below:

'You would use onCacheRemove as the input parameter for the


'CacheItemRemovedCallback parameter in the fourth form of the Insert method
Dim onCacheRemove As CacheItemRemovedCallback
OnCacheRemove = New CacheItemRemoved(AddressOf Me.CheckCallback)
' Now, create the function
Sub CheckCallback (str As String, obj As Object, reason As CacheItemRemovedReason)
Response.Write ("Cache was removed because : " & reason)
End Sub

You might also like