Simple cache interface and implementations

During a couple of my last .Net projects I used the same cache interface and implementations of it with MemoryCache, System.Web.Caching.Cache and CacheManager.

Visual Studio solution is available here https://github.com/mchudinov/CacheNet. Solution includes source code in both C# and Visual Basic.

1. Interfaces

public interface ICache
{
	T Get<T>(string key);
	void Set<T>(string key, T value);
	void SetSliding<T>(string key, T value);
	void Set<T>(string key, T value, int duration);
	void SetSliding<T>(string key, T value, int duration);
	void Set<T>(string key, T value, DateTimeOffset expiration);
	bool Exists(string key);      
	void Remove(string key);
}

Same interface in Visual Basic

Public Interface ICache
    Function [Get](Of T)(key As String) As T
    Sub [Set](Of T)(key As String, value As T)
    Sub SetSliding(Of T)(key As String, value As T)
    Sub [Set](Of T)(key As String, value As T, duration As Integer)
    Sub SetSliding(Of T)(key As String, value As T, duration As Integer)
    Sub [Set](Of T)(key As String, value As T, expiration As DateTimeOffset)    
    Function Exists(key As String) As Boolean
    Sub Remove(key As String)
End Interface

2. Cache provider base abstract class

It defines to useful configuration parameters:

  • CacheDefaultDurationMinutes – default time to live for cache items
  • CacheKeyPrefix – key prefix for all items
public abstract class CacheProviderBase<TCache> : ICache
{
	public int CacheDuration { get; set; }
	protected readonly TCache Cache;
	private const int DefaultCacheDurationMinuts = 30;
	protected readonly string KeyPrefix;

	public CacheProviderBase()
	{
		int result;
		CacheDuration = int.TryParse(ConfigurationManager.AppSettings["CacheDefaultDurationMinutes"], out result) ? result : DefaultCacheDurationMinuts;
		KeyPrefix = !string.IsNullOrEmpty(ConfigurationManager.AppSettings["CacheKeyPrefix"]) ? ConfigurationManager.AppSettings["CacheKeyPrefix"] : string.Empty;
		Cache = InitCache();
	}

	protected abstract TCache InitCache();
....
}

3. Implementation

Implementations for MemoryCache and for System.Web.Caching.Cache are trivial. Source code is available here
https://github.com/mchudinov/CacheNet/blob/master/CacheCSharp/MemoryProvider.cs
https://github.com/mchudinov/CacheNet/blob/master/CacheCSharp/SystemWebProvider.cs

4. Implementation for CacheManager

The important moment with CacheManager is configuration. There are many ways to configure CacheManager, they are described in the documentation. I prefer to configure with a configuration file App|Web.config.

Config file can be transformed while building solution for target environment. With a config file transformations it is easy to implement a scenario with different cache backends in development and in production.

Useful link all valid configurations options in *.config file.

Simple configuration example:

  <cacheManager>
    <managers>
      <cache name="cache" updateMode="Up" enableStatistics="false" enablePerformanceCounters="false">
        <handle name="Memory" ref="SystemRuntimeHandle" />
      </cache>
    </managers>
    <cacheHandles>
      <handleDef id="SystemRuntimeHandle" type="CacheManager.SystemRuntimeCaching.MemoryCacheHandle`1, CacheManager.SystemRuntimeCaching" />
    </cacheHandles>
  </cacheManager>

Cache manager factory uses name “cache” to create an instance of a CacheManager with this configuration.

I use the same name in different configuration files (Web.Release.config and Web.Debug.config), for instance just “cache”. But the configuration can be different for development and production.

public class CacheManagerProvider : CacheProviderBase<ICacheManager<object>>
{    
	protected override ICacheManager<object> InitCache()
	{
		return CacheFactory.FromConfiguration<object>("cache");
	}
      ....
}

5. Serialization

Serialization is needed for non serializable objects for distributed (not in-process) cache systems like Redis and Memcached.
Useful link CacheManager serialization documentation.
To enable caching for non serializable objects we need to use CacheManager.Serialization.Json.
Install this package and add the following line to config for this for the cache manager:
serializerType="CacheManager.Serialization.Json.JsonCacheSerializer, CacheManager.Serialization.Json

6. Extension methods

Sync and async extension for cache. Method checks cache value, if value not present then run a method that is passed as parameter and put the returned result into cache with the given key.

public static async Task UseCachedAsync(this ICache cache, string key, Func<Task> factory, int? duration = null) where T: class
public static T UseCached(this ICache cache, string key, Func factory, int? duration = null) where T : class

Usage:

ICache cache = new SystemWebProvider();
var result = cache.UseCached<string>("key1", obj.Method, 10);
cache.UseCachedAsync<string>("key2", obj.MethodAsync, 10);