views:

341

answers:

4

We are working on a large legacy application and we're redesigning the business layer and the data layer. We believe that it is a good time to redesign the way cache is handled. Are there any patterns and best practices for implementing a caching layer (or building it in the business layer)?

The only things that I can think of are to use SQL Cache Dependency (which is hard because of we've got a big legacy database with a lot of tables that do not always correspond to the business objects) and implementing strongly typed CacheManager class to hide the string keys and eliminate class casting problems.

Is there anything more sophisticated that we can do? Is there any way to enforce cache invalidation when performing update/delete? Should we somehow maintain a single object in the cache and retrieve list of IDs from the DB that always work with the same objects?

Basically what techniques for caching do you use in ASP.NET? Too bad we cannot use IoC containers or ORM frameworks that support cache :(

Edit: I am more concerned about maintainability than performance.

+4  A: 
  • Just save every queryresult to the database (with cache key: your query, value: your list of business objects)
  • Use distributed cache like memcached next to ASP.Net cache
  • Use a sophisticated cachemanager like http://enyimmemcached.codeplex.com/; that can have cache-groups. Some data has to be stored for a long time, some short time. Some data has to be stored in ASP.Net cache, etc.
  • Do calls that has to be stored in the cache using a wrapper function like public T GetFromCache<T>(string key, Func<T> ifKeyNotFoundDelegate) to ensure that cache is always used the same. [1]
  • Think of when to use ASP.Net cache, and when to use distributed cache. Data that is read every request should be stored in ASP.Net, large data like search results; with alot of different keys and data etc. should be in memcached.
  • Add versioning. Prefix all keys with a versionnumber, so you won't get in trouble when updating your web application, and some objectcontracts change.

Ah well, that covers most of what we do in our website (20GB memcached cluster spread over 20 servers).

[1] By making such a function the only interface to store stuff in cache, you can achieve the following. Let's say I want to use something from the cache, like the result from a function. Normally you would do something like

CacheManager cm = new CacheManager(CacheGroups.Totals);
object obj = cm.GetFromCache("function1result");
if(obj == null)
{
    obj = (object)DAO.Foo();
    cm.StoreInCache("function1result", obj);
}
return (List<MyEntity>)obj;

By using a different interface you can ensure that users won't make a mistake here.

Like

public T GetFromCache<T>(string key, Func<T> ifnotfound)
{
    T obj = this.GetFromCache(key) as T;
    if(obj == default(T)) 
    { 
         obj = ifnotfound.Invoke();
         this.StoreInCache(key, obj);
    }
    return obj;
}

This ensures that

  1. We always work with the correct type
  2. That your user always work with cache the same way

Ergo: less probable that they make a mistake. Furthermore: you get nicer, more clear, code, like:

List<MyEntity> list = new CacheManager(CacheGroups.Total).GetFromCache<List<MyEntity>>("function1result", ()=>DAO.Foo());
Jan Jongboom
Thank you very much (and everyone else who replied). Can you elaborate on this:•Do calls that has to be stored in the cache using a wrapper function like public T GetFromCache<T>(string key, Func<T> ifKeyNotFoundDelegate) to ensure that cache is always used the same.
Stilgar
In a multithreaded environment, don't forget a lock in GetFromCache<>().
RickNZ
+1  A: 

The MS Patterns and Practices team created Enterprise Library as their response to this question for a host of commone scenarios. EntLib includes Caching as well as Data Access, Validation, Logging, Exception handling, etc. We've used it for years and wouldn't think of starting a new project without it.

http://www.codeplex.com/entlib

As well as the P&P home page, http://msdn.microsoft.com/en-us/practices/default.aspx

Walter
+1  A: 

velocity/AppFabric is a nice thing! more info here

using sql cache dependencies caused some severe problems in one of our apps.
another projects relies on a special wcf-service (backended: windows-service) to do dataHandling, caching ...

Andreas Niedermair
+1  A: 

This is a big subject, but here are a few suggestions:

  1. Don't cache data that's unlikely to be reused, such as user-specific data
  2. Cache at all tiers: client, Silverlight (isolated storage), proxies, http.sys, IIS, ASP.NET cache object, ASP.NET per-request cache, SQL Server
  3. Use SqlDependency / SqlCacheDependency when you can, but don't over-use
  4. Avoid session state; use cookies instead when you can
  5. Leverage page and control (fragment) output caching
  6. Consider using cache validation when needed
  7. Consider light-weight alternatives to the ASP.NET cache object, such as weak memory refs
  8. When used correctly, SQL Server can act as a large cache

In case it helps, I cover this subject in detail in my book: Ultra-Fast ASP.NET.

RickNZ
What do you mean by weak memory refs? A form of very simple caching I use is to create a static variable like this:private static DataTable _products;public static DataTable products{ get { if(_productes == null){_products = getProductsFromSQLServer();} return _products }}Is this Kosher? I usually only do it for very small record sets.
J.Hendrix
Wow, sorry about that. All of my white-space was removed from the comment.
J.Hendrix
@J.Hendix: For info on weak memory references, look into the WeakReference class (or maybe post a question on StackOverflow). Your example was a strong static reference. That is certainly a valid form of caching. The advantage of a weak ref is that the GC can let go of it if memory pressure gets too high, whereas it can't let go of a strong ref.
RickNZ
Thank you very much for your replies. However I am more interested in patters to allow me to prevent caching bugs, incorrect use of cache by fellow developers, automatic invalidation and so on. At this point I am not looking at very high performance for the cache. I am more concerned about maintainability.
Stilgar
@Stilgar: caching bugs often happen as a result of a faulty architecture, or incorrect design. The Cache object itself is relatively robust, except perhaps in the area of multi-threading and locks. If your site will be load balanced or deployed as a web garden, that opens the door to a whole host of possible problems.
RickNZ