Whenever
an attempt is made to access data from the cache, it should be with
the assumption that the data might not be there any more. Thus, the
following pattern should be universally applied to your access of
cached data. In this case, we're going to assume the object that has
been cached is a DataTable.
public DataTable GetCustomers(bool BypassCache)
{
string cacheKey = "CustomersDataTable";
object cacheItem = Cache[cacheKey] as DataTable;
if((BypassCache) || (cacheItem == null))
{
cacheItem = GetCustomersFromDataSource();
Cache.Insert(cacheKey, cacheItem, null,
DateTime.Now.AddSeconds(GetCacheSecondsFromConfig(cacheKey),
TimeSpan.Zero);
}
return (DataTable)cacheItem;
}
There are several points I'd like to make about this pattern:
- Values, like cacheKey, cacheItem and the cache duration, are defined once and only once.
- The
cache can be bypassed as needed—for example, after registering a new
customer and redirecting to a list of customers, it would probably be
best to bypass the cache and repopulate it with the latest data, which
would include the newly inserted customer.
- Cache
is only accessed once. This has performance benefits and ensures that
NullReferenceExceptions don't occur because the item was present the
first time it was checked but had expired before the second check.
- The
pattern uses strong type checking. The "as" operator in C# will
attempt to cast an object to a type and will simply return null if it
fails or if the object is null.
- The
duration is stored in a configuration file. All cache dependencies,
whether file-based, time-based, or otherwise, should ideally be stored
in a configuration file so that changes can be made and performance
measured easily. I also recommend that a default cache duration be
specified, and that the GetCacheSecondsFromConfig() method uses the
default if no duration is specified for the cacheKey being used.