25 October 2007

Caching In 10 Minutes

This is some stuff I've been meaning to post to the wiki at work because I've been asked a lot of questions about it, but just haven't gotten around to yet.  It's a little bit intro, but I couldn't find a good snapshot anywhere else, so here we are.  (It's also a test post with Windows Live Writer on the new Community Server install so I'm killing two birds with one stone.)

Start the clock now...

I'll do a quick overview of some ASP.Net Caching basics, and post to some resources on getting more information.

There's 3 basic types of "caching" in ASP.Net 2.0, and I'm using the term caching kind of loosely:

  1. Viewstate
  2. Context, Session, and Application
  3. Cache API
  4. HTML Output Caching

Viewstate

Viewstate isn't really caching; it's a state management mechanism used via a hidden form field created by the ASP.Net runtime to persist values on a page between postbacks. 

It's used internally by the worker process to keep track of control property values for server controls between roundtrips.  For postback controls, like a text box, ASP.Net automatically retains the values of the controls in the viewstate in the State Bag dictionary collection.  In classic ASP, this was a lot of code to retain values manually.  For non-postback controls, like a label, ASP.Net manages any of the values that may have been modified in code.  

You can also manually add any serializable items to viewstate:

Viewstate[key] = value;

You can retrieve values by:

value = Viewstate[key];

See Dino's blog or great MSDN article for more info.

Session, Application, and Context

In the strictest sense these aren't really cache objects, but they are collections that can be used to stash objects so that you don't have to repeat expensive operations like calculations, database retrieval, instantiation, etc.  The major differences between them are scope:

  • Context.Items is a collection stored in an IDictionary key/value collection that is available through the entire HttpRequest cycle.  So if you have things that you want to store an pass between layers in an application, like between controls or to a helper library.  Sure you could use properties for this, but it also stays alive if you do a Server.Transfer on the page as well.
    It's scoped to a single request and therefore a single user.
  • Session is a ICollection container that stores information for a single browser session for a limited time (multiple browser windows are usually treated as a single user session).  It's usually stored in the same memory space as the ASP.Net worker process, but options are available for a separate state service and SQL server.  In a server cluster, each machine usually has it's own session if its being stored in memory.  The default timeout is 30 minutes but each request resets the counter.  Session tracking is done via cookie, but only the session ID is stored in the cookie, but the actual session contents are not.  Session can be turned off per page or per application, or set to a read only mode.
    Session state is scoped to a single user per application. 
  • Application is another collection (NameObjectCollectionBase this time) that is pretty much global to an application.  Usually items are added to the collection during the Application_Start method in the global.asax file.  It's possible to add items elsewhere in the application, but this is usually done with Application.Lock to avoid concurrency issues.
    Application is scope to all users and all pages or modules with a single application.

These are basic dictionary types, so they're pretty simple to use.  Check the MSDN State Management section for more info.

Cache API

System.Web.Caching is a mechanism that's new to ASP.Net 2.0 that allows you to store arbitrary objects in a dictionary.  It's scoped to an application so all users share that same cache.  Sounds a lot like the Application collection, but it's got some cool bells and whistles:

  • When adding an item to the cache, you can set absolute or sliding expirations to control how "stale" you want to let your data get.
  • You can set dependencies on files, other cache items, or even SQL data so that your cache gets dumped when one of these things changes.
  • You can set the priority of cached objects so that when the cache needs to be flushed (to recover memory) the lowest items are removed first. 
  • You can specify a delegate method that gets called to notify applications when an item is removed.
  • Of course, you can manually remove items form cache as well.

This is really pretty cool stuff with much more control than any of the previous items.  It's typically used with data retrieved from a database.  Here's the proper way to use it with data retrieval:

public DataTable GetStuff() 
{
string key = “key”;
object cacheItem = myCache[key] as DataTable;
if(cacheItem == null)
{  
cacheItem = DAL.GetData();// fetch from DB/source  
myCache.Insert(key, cacheItem, null, DateTime.Now.Add(Globals.CacheDuration), TimeSpan.Zero);
}
return (DataTable)cacheItem;
}

Steven Smith from ASPAlliance.com seems to be the guru on the subject.  Some great info of his:

  • Do not underestimate the value of even short caching (aka microcaching).  This simple demo shows a 5x increase in throughput from a 1 second cache.
  • There a couple of good getting started/overview videos on www.asp.netSee #3 and #4 in the How Do I section.
  • Once you implement caching on your site, you can manage it using the Cache Manager Plug-in which is a single dll which requires just one web.config entry to implement

HTML Output Caching

This might technically be part of the Cache API, but most a lot of it can be done declaratively, so I'm treating it separately.

Output caching improves performance by storing the rendered markup of a page (or part of a page) and serving the stored content instead of re-rendering the page.  So, if you have a table of items that doesn't change within a known time period, you can pre-fetch that data and pre-render the table instead of doing that each time.

You can implement it on a page simply by using the OutputCache directive at the top of your page or control, thusly:

<%@ Page ... %>
<%@ OutputCache Duration="duration" VaryByParam="paramList" %>

Some highlights:

  • You can cache an entire page or a user control contained with a page using the same OutputCache directive syntax.  Multiple controls on a pge can each have their own durations.
  • The VaryByParam attribute allows you to cache items separately based on parameter values (like querystrings).  Also check out the VaryByControl and VaryByCustom attributes.
  • When output caching a control, by default it is only reused on a per-page basis.  Setting the Shared attribute equal to true allows the controls output to be reused on multiple pages.
  • You can set dependencies on output cache items programmatically using similar techniques to data caching using the Cache API.
  • "Donut caching" is having a section of dynamic markup within a cached control.  You can use the <asp:Substitution> control or the Response.WriteSubstitution method to do this.

One of the really cool things about this is that you may already have controls or can easily refactor UI code to use this.

More Info:

Times Up!

Hope this was worth your 10 minutes.  I know there's lots more, but maybe this will point you in the right direction.

Filed under:

Comments

# DotNetKicks.com said:

You've been kicked (a good thing) - Trackback from DotNetKicks.com

Monday, October 29, 2007 5:26 PM
# Work vs. Play said:

I wrote a quick overview of caching (and state) the other day and saw a great VaryByCustom tip on Steven

Monday, October 29, 2007 9:02 PM
# tamer zahran said:

Good article

Wednesday, October 31, 2007 11:05 AM
# Tiernans Comms Closet said:

Sorry for the lack of posts this week, and i am pre-empting the same this week. So, here are a few quick

Monday, November 05, 2007 1:32 AM
 

News

Hey, somebody remind me to put something good in this space. :)