41

I have the need to cache a collection of objects that is mostly static (might have changes 1x per day) that is avaliable in my ASP.NET Web API OData service. This result set is used across calls (meaning not client call specific) so it needs to be cached at the application level.

I did a bunch of searching on 'caching in Web API' but all of the results were about 'output caching'. That is not what I'm looking for here. I want to cache a 'People' collection to be reused on subsequent calls (might have a sliding expiration).

My question is, since this is still just ASP.NET, do I use traditional Application caching techniques for persisting this collection in memory, or is there something else I need to do? This collection is not directly returned to the user, but rather used as the source behind the scenes for OData queries via API calls. There is no reason for me to go out to the database on every call to get the exact same information on every call. Expiring it hourly should suffice.

Any one know how to properly cache the data in this scenario?

atconway
  • 20,624
  • 30
  • 159
  • 229

2 Answers2

51

The solution I ended up using involved MemoryCache in the System.Runtime.Caching namespace. Here is the code that ended up working for caching my collection:

//If the data exists in cache, pull it from there, otherwise make a call to database to get the data
ObjectCache cache = MemoryCache.Default;

var peopleData = cache.Get("PeopleData") as List<People>;
if (peopleData != null)
   return peopleData ;

peopleData = GetAllPeople();
CacheItemPolicy policy = new CacheItemPolicy {AbsoluteExpiration = DateTimeOffset.Now.AddMinutes(30)};
cache.Add("PeopleData", peopleData, policy);
return peopleData;

Here is another way I found using Lazy<T> to take into account locking and concurrency. Total credit goes to this post: How to deal with costly building operations using MemoryCache?

private IEnumerable<TEntity> GetFromCache<TEntity>(string key, Func<IEnumerable<TEntity>> valueFactory) where TEntity : class 
{
    ObjectCache cache = MemoryCache.Default;
    var newValue = new Lazy<IEnumerable<TEntity>>(valueFactory);            
    CacheItemPolicy policy = new CacheItemPolicy { AbsoluteExpiration = DateTimeOffset.Now.AddMinutes(30) };
    //The line below returns existing item or adds the new value if it doesn't exist
    var value = cache.AddOrGetExisting(key, newValue, policy) as Lazy<IEnumerable<TEntity>>;
    return (value ?? newValue).Value; // Lazy<T> handles the locking itself
}
Community
  • 1
  • 1
atconway
  • 20,624
  • 30
  • 159
  • 229
  • 1
    how to call this GetFromCache method?what i need to paas in valueFactory parameter?If i have to store output of Get which calls repository,how to use it and then use it in subsequent call? – F11 Oct 27 '14 at 03:52
27

Yes, output caching is not what you are looking for. You can cache the data in memory with MemoryCache for example, http://msdn.microsoft.com/en-us/library/system.runtime.caching.memorycache.aspx . However, you will lose that data if the application pool gets recycled. Another option is to use a distributed cache like AppFabric Cache or MemCache to name a few.

Pablo Cibraro
  • 3,769
  • 3
  • 25
  • 17
  • 2
    Yes after writing this I found `MemoryCache` in the `System.Runtime.Caching` namespace that looks perfect. The caveat that I could loose the data if AppPool recycles is OK because I'm just trying to prevent re-calling the DB on *every* API call. Using the `MemoryCache.Get()` and `MemoryCache.Add()` seem to be working well for me. `AddOrGetExisting()` looked cool but didn't work for my situation; it would have still required getting data from DB first before calling. I set an `AbsoluteExpiration` of 30 minutes. – atconway May 08 '13 at 17:35
  • 5
    Just as a marker to help people along the way, the recommended caching technology in Azure is now Redis. Redis or another distributed cache is required if you have more than 1 instance of your API. Azure Managed Cache is now very hidden, accessible only from Powershell. They are just keeping it there to help people who previous used it. – Adam May 28 '15 at 10:07
  • 1
    Redis is too expensive if all you are doing is caching a few WebApi data calls. – Larry Flewwelling Feb 02 '17 at 21:47