26

Aug

Un comodo CacheManager per .NET 4

Ciao a tutti

 

vediamo come creare un comodo cachemanager per gestire gli scenari più frequenti del caching con il MemoryCache di .NET 4 (no client profile)

se invece siete interessati alla cache distribuita: http://dotnetlombardia.org/b/tonyexpo/archive/2010/10/15/appfabric-velocity-distributed-cache.aspx

 

la classe di cache:

 

public class CacheManager
{
    internal readonly MemoryCache Cache;

    //inizializzo il memorycache con l'hashcode dell'istanza così da poter usare diverse cache
    public CacheManager() { Cache = new MemoryCache(GetHashCode().ToString()); }

    internal virtual string Key(Func driller, string[] additionalkeys)
    {
        //l'hash di un delegate non cambia mai finchè si usa lo stesso metodo anonimo
        string k = driller.GetHashCode().ToString();
        if (additionalkeys.Length > 0)
        {
            var b = new StringBuilder(k);
            b.Append("-");
            foreach (var s in string.Join(";", additionalkeys))
                b.Append(s);
            return b.ToString();
        }
        else
            return k;
    }

    public T GetOrDrill(Func driller, DateTime absoluteExpiration, params string[] additionalkeys)
    {
        var key = Key(driller, additionalkeys);
        T risp = (T)Cache[key];

        if (risp == null)
        {
            risp = driller();
            System.Diagnostics.Debug.WriteLine("Adding to cache ID " + key);
            Cache.Add(key, risp, new CacheItemPolicy() { AbsoluteExpiration = absoluteExpiration, RemovedCallback = OnRemovedFromCache });
        }

        return risp;
    }

    public T GetOrDrill(Func driller, TimeSpan slidingExpiration, params string[] additionalkeys)
    {
        var key = Key(driller, additionalkeys);
        T risp = (T)Cache[key];

        if (risp == null)
        {
            risp = driller();
            System.Diagnostics.Debug.WriteLine("Adding to cache ID " + key);
            Cache.Add(key, risp, new CacheItemPolicy() { SlidingExpiration = slidingExpiration, RemovedCallback = OnRemovedFromCache });
        }

        return risp;
    }

    internal virtual void OnRemovedFromCache(CacheEntryRemovedArguments e)
    {
        System.Diagnostics.Debug.WriteLine("Removing from cache ID " + e.CacheItem.Key);

        if (e.CacheItem is IDisposable)
        {
            ((IDisposable)e.CacheItem).Dispose();
            System.Diagnostics.Debug.WriteLine("Disposed cache entry!");
        }
    }

    public virtual T GetOrDrillShort(Func driller, params string[] additionalkeys)
    {
        return GetOrDrill(driller, DateTime.Now.AddMinutes(1), additionalkeys);
    }

    public virtual T GetOrDrillLong(Func driller, params string[] additionalkeys)
    {
        return GetOrDrill(driller, DateTime.Now.AddMinutes(10), additionalkeys);
    }

    public virtual T GetOrDrillShortSliding(Func driller, params string[] additionalkeys)
    {
        return GetOrDrill(driller, TimeSpan.FromMinutes(1), additionalkeys);
    }

    public virtual T GetOrDrillLongSliding(Func driller, params string[] additionalkeys)
    {
        return GetOrDrill(driller, TimeSpan.FromMinutes(10), additionalkeys);
    }
}

 

Questa classe ci permette di accedere con facilità al motore di cache tramite metodi anonimi. es:

 

class Program
{
    private readonly static CacheManager Cache = new CacheManager();

    private static byte[] GetCachedBytes()
    {
        return Cache.GetOrDrillShort(() =>
        {
            //questo codice viene eseguito in modalità cached
            var r = new Random(DateTime.Now.Millisecond);
            var b = new byte[10000000];
            r.NextBytes(b);
            return b;
        });
    }

    private static byte[] GetCachedBytes(int i)
    {
        return Cache.GetOrDrillShort(() =>
        {
            //questo codice viene eseguito in modalità cached
            var r = new Random(DateTime.Now.Millisecond);
            //se invece alcuni parametri vengono dall'esterno, è necessario passarli anche in chiave
            //per evitare perdite di dati
            var b = new byte[1000000 * i];
            r.NextBytes(b);
            return b;
        }, i.ToString()); //l'aggiunta di chiave       
    }

    private static byte[] GetOtherCachedBytes()
    {
        return Cache.GetOrDrillShort(() =>
        {
            //questo codice viene eseguito in modalità cached
            return new byte[] { 1, 2, 3 };
        }, "GetOtherCachedBytes");
    }

    static void Main(string[] args)
    {
        for (int i = 0; i < 10; i++)
        {
            DateTime d = DateTime.Now;
            var bytes = GetCachedBytes();
            Console.WriteLine("{0:N0} items in {1:N0}s", bytes.Length, (DateTime.Now - d).TotalSeconds);
        }

        for (int i = 0; i < 10; i++)
        {
            DateTime d = DateTime.Now;
            var bytes = GetCachedBytes(i);
            Console.WriteLine("{0:N0} items in {1:N0}s", bytes.Length, (DateTime.Now - d).TotalSeconds);
        }

        var str = Cache.GetOrDrillShort(() =>
        {
            //questo codice viene eseguito in modalità cached
            return DateTime.Now.ToString();
        });

        //adesso l'hash del secondo metodo anonimo è cambiato perchè è cambiato il tipo di ritorno!
        //questo è impotante per capire che bisogna sempre prevedere una chiave univoca per
        //lo stesso cachemanager se è questo in comportamento che vogliamo!
        //consiglio comunque di mettere i metodi cached in proprietà o metodi appunto, ed usare
        //i metodi anonimi solo come driller

        var otherbytes = GetOtherCachedBytes();

        Console.WriteLine(str);
        Console.ReadLine();
    }
}

 

per vedere l’output della debug console, è possibile usare la finestra Output di VS.

questo l’output sul mio PC:

 

Adding to cache ID 42865679 (il primo senza parametri)
Adding to cache ID 42865679-0 (con i parametri)
Adding to cache ID 42865679-1
Adding to cache ID 42865679-2
Adding to cache ID 42865679-3
Adding to cache ID 42865679-4
Adding to cache ID 42865679-5
Adding to cache ID 42865679-6
Adding to cache ID 42865679-7
Adding to cache ID 42865679-8
Adding to cache ID 42865679-9
Adding to cache ID 44374744 (la stringa, diverso ritorno, diverso hash)
Adding to cache ID 42865679-GetOtherCachedBytes (ancora bytes, quindi stessa base, ma con parametro di chiave diverso)

spero di avervi dato qualche buona idea Sorriso

 

 

a presto

by Antonio Esposito on 8/26/2011