Wednesday, September 5, 2018

Using Caching in the .NET Framework

Caching is easy to use in the .NET Framework

According to Microsoft:
"In the .NET Framework 3.5 and earlier versions, ASP.NET provided an in-memory cache implementation in the System.Web.Caching namespace. In previous versions of the .NET Framework, caching was available only in the System.Web namespace and therefore required a dependency on ASP.NET classes. In the .NET Framework 4, the System.Runtime.Caching namespace contains APIs that are designed for both Web and non-Web applications."

Here are a couple methods to Load and Save from the Memory Cache.  If the value is not found, it will be null.  

public static class Cache
{
        public static TResult Load<TResult>(string cacheKeyName)
        {
            ObjectCache cacheInstance = MemoryCache.Default;
            return (TResult)cacheInstance[cacheKeyName];
        }

        public static T Save<T> (string cacheKeyName, DateTimeOffset expirationDate, T value)
        {
            CacheItemPolicy cacheItemPolicy = new CacheItemPolicy();
            cacheItemPolicy.AbsoluteExpiration = expirationDate;
            ObjectCache cacheInstance = MemoryCache.Default;
            cacheInstance.Set(cacheKeyName, value, cacheItemPolicy);
            return value;
        }
}

Example Call to Load and Save


Here is an example of using the load and save with a stopwatch so that the time saved can be shown:


        [Test]
        public void SimpleLoadAndSaveTest()
        {
            Stopwatch watch = Stopwatch.StartNew();
            string json = Cache.Load<string>("JsonValue");

            if (json == null)
            {
                json = GetJsonFromWebService("cf4174ce-96d2-495c-b58a-427d35f64a20");
                Cache.Save("JsonValue", DateTimeOffset.Now.AddMinutes(20), json);
            }
            watch.Stop();
            Console.WriteLine(watch.ElapsedMilliseconds);

            watch.Restart();
            json = Cache.Load<string>("JsonValue");
            watch.Stop();
            Console.WriteLine(watch.ElapsedMilliseconds);
        }

        public string GetJsonFromWebService(string value)
        {
            Thread.Sleep(1000);
            return "{ \"Value\" : \"" + value + "\" }";
        }

Cache Class


I have created a larger caching class where methods can be passed into shorten the code:


    public static class Cache
    {
        public static TResult Load<TResult>(string cacheKeyName)
        {
            ObjectCache cacheInstance = MemoryCache.Default;
            return (TResult)cacheInstance[cacheKeyName];
        }

        public static T Save<T> (string cacheKeyName, DateTimeOffset expirationDate, T value)
        {
            CacheItemPolicy cacheItemPolicy = new CacheItemPolicy();
            cacheItemPolicy.AbsoluteExpiration = expirationDate;
            ObjectCache cacheInstance = MemoryCache.Default;
            cacheInstance.Set(cacheKeyName, value, cacheItemPolicy);
            return value;
        }


        public static TResult Load<TResult>(string cacheKeyName, int expirationMinutes, Func<TResult> serviceFunction)
        {
            return Load<TResult>(cacheKeyName, DateTimeOffset.Now.AddMinutes(expirationMinutes), serviceFunction);
        }

        public static TResult Load<TResult>(string cacheKeyName, int expirationMinutes, Func<string, TResult> serviceFunction, string stringArg)
        {
            return Load<TResult>(cacheKeyName, DateTimeOffset.Now.AddMinutes(expirationMinutes), serviceFunction, stringArg);
        }

        public static TResult Load<TResult>(string cacheKeyName, int expirationMinutes, Func<long, TResult> serviceFunction, long longArg)
        {
            return Load<TResult>(cacheKeyName, DateTimeOffset.Now.AddMinutes(expirationMinutes), serviceFunction, longArg);
        }

        public static TResult Load<TResult>(string cacheKeyName, int expirationMinutes, Func<int, TResult> serviceFunction, int intArg)
        {
            return Load<TResult>(cacheKeyName, DateTimeOffset.Now.AddMinutes(expirationMinutes), serviceFunction, intArg);
        }

        public static TResult Load<TResult>(string cacheKeyName, DateTime expirationDate, Func<TResult> serviceFunction)
        {
            return Load<TResult>(cacheKeyName, new DateTimeOffset(expirationDate), serviceFunction);
        }

        public static TResult Load<TResult>(string cacheKeyName, DateTime expirationDate, Func<string, TResult> serviceFunction, string stringArg)
        {
            return Load<TResult>(cacheKeyName, new DateTimeOffset(expirationDate), serviceFunction, stringArg);
        }

        public static TResult Load<TResult>(string cacheKeyName, DateTime expirationDate, Func<long, TResult> serviceFunction, long longArg)
        {
            return Load<TResult>(cacheKeyName, new DateTimeOffset(expirationDate), serviceFunction, longArg);
        }

        public static TResult Load<TResult>(string cacheKeyName, DateTime expirationDate, Func<int, TResult> serviceFunction, int intArg)
        {
            return Load<TResult>(cacheKeyName, new DateTimeOffset(expirationDate), serviceFunction, intArg);
        }

        public static TResult Load<TResult>(string cacheKeyName, DateTimeOffset expirationDateOffset, Func<TResult> serviceFunction) 
        {
            TResult cachedObject = Load<TResult>(cacheKeyName);

            if (cachedObject == null)
            {                
                cachedObject = Save(cacheKeyName, expirationDateOffset, serviceFunction());
            }

            return cachedObject;
        }

        public static TResult Load<TResult>(string cacheKeyName, DateTimeOffset expirationDateOffset, Func<string, TResult> serviceFunction, string stringArg)
        {
            TResult cachedObject = Load<TResult>(cacheKeyName);

            if (cachedObject == null)
            {
                cachedObject = Save(cacheKeyName, expirationDateOffset, serviceFunction(stringArg));
            }

            return cachedObject;
        }

        public static TResult Load<TResult>(string cacheKeyName, DateTimeOffset expirationDateOffset, Func<long, TResult> serviceFunction, long longArg)
        {
            TResult cachedObject = Load<TResult>(cacheKeyName);

            if (cachedObject == null)
            {
                cachedObject = Save(cacheKeyName, expirationDateOffset, serviceFunction(longArg));
            }

            return cachedObject;
        }

        public static TResult Load<TResult>(string cacheKeyName, DateTimeOffset expirationDateOffset, Func<int, TResult> serviceFunction, int intArg)
        {
            TResult cachedObject = Load<TResult>(cacheKeyName);

            if (cachedObject == null)
            {
                cachedObject = Save(cacheKeyName, expirationDateOffset, serviceFunction(intArg));
            }

            return cachedObject;
        }

    }

Example using Func with Caching

The function can be passed in that will fill the data if it does not exist in the cache.

        [Test]
        public void CacheTestUsingFunc()
        {
            Stopwatch watch = Stopwatch.StartNew();
            string json = Cache.Load<string>("JsonValue", 20, GetJsonFromWebService, "cf4174ce-96d2-495c-b58a-427d35f64a20");
            watch.Stop();
            Console.WriteLine(watch.ElapsedMilliseconds);

            watch.Restart();
            json = Cache.Load<string>("JsonValue", 20, GetJsonFromWebService, "cf4174ce-96d2-495c-b58a-427d35f64a20");
            watch.Stop();
            Console.WriteLine(watch.ElapsedMilliseconds);
        }

        public string GetJsonFromWebService(string value)
        {
            Thread.Sleep(1000);
            return "{ \"Value\" : \"" + value + "\" }";
        }