Collections and caching. Don't hit the database, hit memory.

What's a collection? The dumb answer is that collections are objects that implement ICollection. A slightly better answer would be to say that collection is a group of instances of any given object type or struct. One customer is an instance, many customers is a collection.

There are a many generic built-in collections including ArrayList, SortedList, and HashTable, Stack and Queue, and then a bunch of specialized ones like ControlCollection, HttpSessionState, and DataGridItemCollection. Or you can create your own. For example, I now use Data Access Layer classes which implement collections instead of DataTables and DataSets to cache records in memory. CodeSmith has some great templates for automatically generating DAL Classes against tables.

Anyway, the nice thing about Cache, Session and Application is that you can store any sort of object in them, including a collection. Or a string array. A string array isn't a proper "collection" because it doesn't implement .Count(), but it works well for simple lists you might want to keep around. Let's say you wanted to keep a list alive for the lifespan of the Application:

// store
string[] myLanguages = new string[1]; 
myLanguages[0] = "English";
myLanguages[1] = "French";
Application("myLanguages")= myLanguages;
// retrieve
myLanguages = (String[])Application("myLanguages");

On the retrieve I just cast to the sort of object I'm pulling out et voila, regardez l'array. In the above example, you can can freely replace "Application" with "Session" if you want to pin this data to a single client session rather than the Application where is available to all users.

You could also replace "Application" with "Cache" but caches can disappear during a garbage collection so you need something a little different. Let's wrap it in a simple method:

public myNS.myClass.ClientCollection GetClientCollection(Boolean invalidate){
  // Try retrieving the data from the cache
  myNS.myClass.ClientCollection cClients = (myNS.myClass.ClientCollection)Cache("ClientList");
  if ((cClients==null) || (invalidate)) {
    // If it isn't in the cache or the caller wants fresh data then reload the data and cache it again.
    cClients = myNS.myClass.LoadClientCollection();
    Cache("ClientList") = cClients;
  }
}

Again, the big difference with a cache is that it can be garbage collected, and unlike the Application or Session objects you can set up automatic rules to invalidate it when "something happens." Therefore when loading data from a cache you need to do the test for null (see above), and if the cache was invalidated or garbage collected you reload the data and stuff it back into the cache.

You can set either time dependencies (refresh every 15 minutes) or file dependencies (invalidate when a file, perhaps an .xml or .mdb, changes). Shoot, I thought I already blogged about how to invalidate a whole set of cached objects when a record is updated using a Key-based dependency, I guess that's next.

[2003-12-31: Posted a follow-up]

No Comments