Caching in .NET
In every application you have some data that is more read-more, write-once or twice. For example you can have the list of Cities of a country, the list of Countries of the world or list of exchange currency. This data is modified rarely. Also, you can have data that is not very sensitive to be real-time , such as the list of invoices for the day.
In .NET 3.5 you have several options
1. ASP.NET caching – and implementing in other applications with HttpRuntime ( even if MS says “The Cache class is not intended for use outside of ASP.NET applications”)
2. Enterprise caching block : hard to configure
3. memcached , velocity, sharedcache and other third party providers – that comes with their options and configuration
In .NET 4.0 you have a new kid : objectcache and , more important , an implementation : MemoryCache
http://msdn.microsoft.com/en-us/library/system.runtime.caching.memorycache%28v=VS.100%29.aspx
What is very good is that now you can cache in Memory what do you want – and apply easily to your Business Layer. More, the object is a singleton for the application – that is even better (see the test on the final of the post)
What it is missing is an easy implementation for List and an implementation to remove data after a defined time.
So I decided to do my implementation for that (ok, it is wrong to have both implementations in a single class – but you can separate easily )
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Runtime.Caching; namespace CachingData{ /// <summary> /// List<int> i = new List<int>() { 1, 10, 100 }; //CacheData_List<List<int>, int> data = new CacheData_List<List<int>, int>(2); //data.Add(i); //Assert.AreEqual(3, data.Items().Count, "must be 3"); //data = new CacheData_List<List<int>, int>(); //Assert.AreEqual(3, data.Items().Count, "must be 3"); //Assert.IsTrue(data.ContainsData, "must have data"); //Thread.Sleep(1000 * 3); //Assert.IsFalse(data.ContainsData, "must not have data"); /// </summary> /// <typeparam name="T">and generic ILIST </typeparam> /// <typeparam name="U"></typeparam> public class CacheData_List<T, U> where T:class,IList<U>, new() { /// <summary> /// use this for adding in cache /// </summary> public event EventHandler RemovedCacheItem; private System.Timers.Timer timerEvent; private MemoryCache buffer; private int TimeToLiveSeconds; private DateTimeOffset dto; private string Key; /// <summary> /// default constructor - cache 600 seconds = 10 minutes /// </summary> public CacheData_List() : this(600) { } /// <summary> /// constructor cache the mentioned TimeSeconds time /// </summary> /// <param name="TimeSeconds">value of time for cache</param> public CacheData_List(int TimeSeconds) { TimeToLiveSeconds = TimeSeconds; timerEvent=new System.Timers.Timer(TimeToLiveSeconds * 1000); timerEvent.AutoReset = true; timerEvent.Elapsed += new System.Timers.ElapsedEventHandler(timerEvent_Elapsed); dto = new DateTimeOffset(DateTime.UtcNow.AddSeconds(TimeToLiveSeconds)); Key = typeof(T).ToString(); buffer = MemoryCache.Default; } void timerEvent_Elapsed(object sender, System.Timers.ElapsedEventArgs e) { if (RemovedCacheItem != null) { RemovedCacheItem(this, EventArgs.Empty); } } /// <summary> /// remove item from cache /// </summary> public void Remove() { if (buffer.Contains(Key)) { buffer.Remove(Key); } dto=new DateTimeOffset(DateTime.UtcNow.AddSeconds(TimeToLiveSeconds)); } /// <summary> /// add multiple items to cache /// </summary> /// <param name="items">items to add to the cache</param> public void Add(T items) { if (buffer.Contains(Key)) { T data = Items(); foreach (var t in data) { items.Add(t); } buffer.Remove(Key); } buffer.Add(Key, items, dto); } /// <summary> /// add a item to the IList of the cache /// </summary> /// <param name="item">an item to add</param> public void AddItem(U item) { T data=new T(); if (buffer.Contains(Key)) { data = buffer.Get(Key) as T; buffer.Remove(Key); } data.Add(item); buffer.Add(Key, data,dto); } /// <summary> /// usefull if you do not intercept the removed event /// </summary> public bool ContainsData { get { return buffer.Contains(Key); } } /// <summary> /// retrieve items /// </summary> /// <returns></returns> public T Items() { if (!buffer.Contains(Key)) return null; return buffer.Get(Key) as T; } } }
Please note that the test for usage is in the summary :
</pre> List i = new List() { 1, 10, 100 }; CacheData_List <List<int>, int> data = new CacheData_List<List<int>, int>(2); data.Add(i); Assert.AreEqual(3, data.Items().Count, "must be 3"); data = new CacheData_List<List<int>, int>(); Assert.AreEqual(3, data.Items().Count, "must be 3"); Assert.IsTrue(data.ContainsData, "must have data"); Thread.Sleep(1000 * 3); Assert.IsFalse(data.ContainsData, "must not have data");
You can download the file from
http://msprogrammer.serviciipeweb.ro/wp-content/uploads/2010/05/CachingData.zip
(Last Note : for synchonization maybe it was better to lock on readerwriterlockslim
great post as usual!
Great job!
That’s will really rocks inluding regions!!
Nu vad de ce e necesara neaparat o lista. De obicei in cazul unui cache ai o cheie si vrei sa obtii obiectul daca a fost cacheuit.
Independent de asta, mi se pare ca reinventezi roata:
http://msdn.microsoft.com/en-us/library/system.runtime.caching.cacheitempolicy.absoluteexpiration%28v=VS.100%29.aspx
http://msdn.microsoft.com/en-us/library/dd780569%28v=VS.100%29.aspx
Cand adaugi un item in Cache poti sa ii setezi prin policy timeout-ul pe care tu il faci manual.
Mai mult, poti avea timeout-uri diferite per itemuri & poti avea si un sliding timeout (e pastrat in cache cu conditia sa fie accesat intr-o perioada de timp)
Se pare ca intr-adevar re-inventez roata un pic …dar pentru liste … O sa ma gindesc si o sa refac codul…
Nice post but I always cringe when I see seconds passed as ints. That’s what TimeSpan is there for!