Programming Microsoft ASP.NET 4 - Dino Esposito [382]
In the end, you should be considering caching all the time and filter it out in favor of direct data access only in special situations. As a practical rule, when users claim they need live data, you should try to provide a counterexample that proves to them that a few seconds of delay are still acceptable and that the delay can maximize hardware and software investments.
Fetching the real data is an option, but it might be the most expensive one. If you choose that option, make sure you really need it. Accessing cached data is usually faster if the data you get in this way makes sense to the application. On the other hand, be aware that caching requires memory. If abused, it can lead to out-of-memory errors and performance hits. Having said that, don’t be too surprised if you find out that sometimes fetching data is actually faster than accessing items in a busy cache. This is due to how optimized SQL Server access has gotten these days.
Building a Wrapper Cache Object
As mentioned, no data stored in the ASP.NET cache is guaranteed to stay there when a piece of code attempts to read it. For the safety of the application, you should never rely on the value returned by the Get method or the Item property. The following pattern keeps you on the safe side:
var data = Cache["MyData"];
if (data != null)
{
// The data is here, process it
...
}
The code snippet deliberately omits the else branch. What should you do if the requested item is null? You can abort the ongoing operation and display a friendly message to the user, or you can perhaps reload the data with a new fetch. Whatever approach you opt for, it will hardly fit for just any piece of data you can have in the cache.
If you need the cache as a structural part of the application (rather than just for caching only a few individual pieces of data), it has to be strictly related to the data access layer (DAL) and the repository interfaces you have on top of that. (See Chapter 14.) Depending on the pattern you prefer, you can have caching implemented as a service in the business tier (Cache-side pattern) or integrated in the DAL and transparent to the rest of the application (Cache Through pattern). Figure 18-2 shows the resulting architecture in both cases.
Figure 18-2. Isolating the caching layer.
In addition, you need to consider the pluggability of the caching layer. Whether you design it as an application service or as an integral part of the DAL, the caching service must be abstracted to an interface so that it can be injected in the application or in the DAL. At a minimum, the abstraction will offer the following:
public interface ICacheService
{
Object Get(String key);
void Set(String key, Object data);
...
}
You are responsible for adding dependencies and priorities as appropriate. Here’s the skeleton of a class that implements the interface using the native ASP.NET Cache object:
public class AspNetCacheService : ICacheService
{
public Object Get(String key)
{
return HttpContext.Current.Cache[key];
}
public void Set(String key, Object data)
{
HttpContext.Current.Cache[key] = data;
}
...
}
As emphatic as it might sound, you should never use the Cache object directly from code-behind classes in a well-designed, ASP.NET-based layered solution.
Clearing the Cache
The .NET Framework provides no method on the Cache class to programmatically clear all the content. The following code snippet shows how to build one:
public void Clear()
{
foreach(DictionaryEntry