feanz
1/2/2014 - 9:14 AM

Cache Infrastucture

Cache Infrastucture

/// <summary>
	/// <see cref="ICache"/> implementation which does nothing
	/// </summary>
	/// <remarks>
	/// Used when real caches are unavailable or disabled
	/// </remarks>
	public class NullCache : CacheBase
	{
		public NullCache(ICacheConfiguration cacheConfiguration)
			: base(cacheConfiguration)
		{

		}

		public override CacheType CacheType
		{
			get { return CacheType.Null; }
		}

		protected override void InitialiseInternal()
		{
		}

		protected override void SetInternal(string key, object value)
		{
		}

		protected override void SetInternal(string key, object value, DateTime expiresAt)
		{
		}

		protected override void SetInternal(string key, object value, TimeSpan validFor)
		{
		}

		protected override object GetInternal(string key)
		{
			return null;
		}

		protected override void RemoveInternal(string key)
		{
		}

		protected override bool ExistsInternal(string key)
		{
			return false;
		}
	}
public interface ICache
	{
		/// <summary>
		/// Returns the type of the cache
		/// </summary>
		CacheType CacheType { get; }

		/// <summary>
		/// Performs initialisation tasks required for the cache implementation
		/// </summary>
		void Initialise();

		/// <summary>
		/// Insert or update a cache value
		/// </summary>
		/// <param name="key"></param>
		/// <param name="value"></param>
		void Set(string key, object value);

		/// <summary>
		/// Insert or update a cache value with an expiry date
		/// </summary>
		/// <param name="key"></param>
		/// <param name="value"></param>
		/// <param name="expiresAt"></param>
		void Set(string key, object value, DateTime expiresAt);

		/// <summary>
		/// Insert or update a cache value with a fixed lifetime
		/// </summary>
		/// <param name="key"></param>
		/// <param name="value"></param>
		/// <param name="validFor"></param>
		void Set(string key, object value, TimeSpan validFor);

		/// <summary>
		/// Retrieve a value from cache
		/// </summary>
		/// <param name="key"></param>
		/// <returns>Cached value or null</returns>
		object Get(Type type, string key);

		/// <summary>
		/// Retrieve a typed value from cache
		/// </summary>
		/// <typeparam name="T"></typeparam>
		/// <param name="key"></param>
		/// <returns></returns>
		T Get<T>(string key);

		/// <summary>
		/// Removes the value for the given key from the cache
		/// </summary>
		/// <param name="key"></param>
		void Remove(string key);

		/// <summary>
		/// Returns whether the cache contains a value for the given key
		/// </summary>
		/// <param name="key"></param>
		/// <returns></returns>
		bool Exists(string key);
	}
public enum CacheType
	{
		/// <summary>
		/// No cache type set
		/// </summary>
		Null = 0,

		Memory,

		Http,
		//AppFabric,
		//Memcached,
		//AzureTableStorage,
		//Disk
	}
[Trait("CacheTests", "CacheQueryHandlerDecorator")]
	public class CacheQueryHandlerDecoratorTests : TestBase
	{
		[Theory, AutoMoqDataCacheConfig]
		public void CacheDecorator_should_call_cache_factory_getting_caheType_defined_on_cache_attribute(
			[Frozen] Mock<IQueryHandler<FakeCacheQuery, FakeResult>> handlerMock,
			[Frozen] Mock<ICacheFactory> cacheFactoryMock,
			FakeCacheQuery query,
			CacheQueryHandlerDecorator<FakeCacheQuery, FakeResult> sut)
		{
			sut.Handle(query);

			cacheFactoryMock.Verify(handler => handler.GetCache(CacheType.Http), Times.Once());
		}

		[Theory, AutoMoqDataCacheConfig]
		public void CacheDecorator_should_call_get_on_cache_handler_if_cache_factory_returns_non_null_cache(
			[Frozen] Mock<ICache> cacheMock,
			[Frozen] Mock<IQueryHandler<FakeCacheQuery, FakeResult>> handlerMock,
			[Frozen] Mock<ICacheFactory> cacheFactoryMock,
			FakeCacheQuery query,
			CacheQueryHandlerDecorator<FakeCacheQuery, FakeResult> sut)
		{
			cacheMock.Setup(cache => cache.CacheType).Returns(() => CacheType.Memory);
			cacheFactoryMock.Setup(factory => factory.GetCache(It.IsAny<CacheType>())).Returns(() => cacheMock.Object);

			sut.Handle(query);

			cacheMock.Verify(handler => handler.Get<FakeResult>(It.IsAny<string>()), Times.Once());
		}

		[Theory, AutoMoqDataCacheConfig]
		public void CacheDecorator_should_call_handle_if_cache_factory_returns_null(
			[Frozen] Mock<IQueryHandler<FakeCacheQuery, FakeResult>> handlerMock,
			[Frozen] Mock<ICacheFactory> cacheFactoryMock,
			FakeCacheQuery query,
			CacheQueryHandlerDecorator<FakeCacheQuery, FakeResult> sut)
		{
			cacheFactoryMock.Setup(factory => factory.GetCache(It.IsAny<CacheType>())).Returns(() => null);

			sut.Handle(query);

			handlerMock.Verify(handler => handler.Handle(query), Times.Once());
		}

		[Theory, AutoMoqDataCacheConfig]
		public void CacheDecorator_should_call_handler_if_cache_factory_returns_null_cache_type(
			[Frozen] Mock<ICache> cacheMock,
			[Frozen] Mock<IQueryHandler<FakeCacheQuery, FakeResult>> handlerMock,
			[Frozen] Mock<ICacheFactory> cacheFactoryMock,
			FakeCacheQuery query,
			CacheQueryHandlerDecorator<FakeCacheQuery, FakeResult> sut)
		{
			cacheMock.Setup(cache => cache.CacheType).Returns(() => CacheType.Null);
			cacheFactoryMock.Setup(factory => factory.GetCache(It.IsAny<CacheType>())).Returns(() => cacheMock.Object);

			sut.Handle(query);

			handlerMock.Verify(handler => handler.Handle(query), Times.Once());
		}

		[Theory, AutoMoqDataCacheConfig]
		public void CacheDecorator_should_call_handler_if_cache_is_disabled_on_query_cache_attribute(
			[Frozen] Mock<IQueryHandler<FakeCacheDisabledQuery, FakeResult>> handlerMock,
			FakeCacheDisabledQuery query,
			CacheQueryHandlerDecorator<FakeCacheDisabledQuery, FakeResult> sut)
		{
			sut.Handle(query);

			handlerMock.Verify(handler => handler.Handle(query), Times.Once());
		}

		[Theory, AutoMoqDataCacheConfig]
		public void CacheDecorator_should_call_handler_if_cache_is_globally_disabled(
			[Frozen] Mock<IQueryHandler<FakeQuery, FakeResult>> handlerMock,
			[Frozen] Mock<ICacheConfiguration> cacheConfigMock,
			FakeQuery query,
			CacheQueryHandlerDecorator<FakeQuery, FakeResult> sut)
		{
			cacheConfigMock.Setup(configuration => configuration.Enabled).Returns(false);

			sut.Handle(query);

			handlerMock.Verify(handler => handler.Handle(query), Times.Once());
		}

		[Theory, AutoMoqDataCacheConfig]
		public void CacheDecorator_should_call_handler_if_cache_returns_null(
			[Frozen] Mock<ICache> cacheMock,
			[Frozen] Mock<IQueryHandler<FakeCacheQuery, FakeResult>> handlerMock,
			FakeCacheQuery query,
			CacheQueryHandlerDecorator<FakeCacheQuery, FakeResult> sut)
		{
			cacheMock.Setup(cache => cache.Get<FakeResult>(It.IsAny<string>())).Returns(() => null);

			sut.Handle(query);

			handlerMock.Verify(handler => handler.Handle(query), Times.Once());
		}

		[Theory, AutoMoqDataCacheConfig]
		public void CacheDecorator_should_call_handler_if_result_is_not_decorated_with_cache_attribute(
			[Frozen] Mock<IQueryHandler<FakeQuery, FakeResult>> handlerMock,
			[Frozen] Mock<ICacheConfiguration> cacheConfigMock,
			FakeQuery query,
			CacheQueryHandlerDecorator<FakeQuery, FakeResult> sut)
		{
			sut.Handle(query);

			handlerMock.Verify(handler => handler.Handle(query), Times.Once());
		}

		[Theory, AutoMoqDataCacheConfig]
		public void CacheDecorator_should_not_call_cache_if_cache_factory_returns_null_cache_type(
			[Frozen] Mock<ICache> cacheMock,
			[Frozen] Mock<ICacheFactory> cacheFactoryMock,
			FakeCacheQuery query,
			CacheQueryHandlerDecorator<FakeCacheQuery, FakeResult> sut)
		{
			cacheMock.Setup(cache => cache.CacheType).Returns(() => CacheType.Null);
			cacheFactoryMock.Setup(factory => factory.GetCache(It.IsAny<CacheType>())).Returns(() => cacheMock.Object);

			sut.Handle(query);

			cacheMock.Verify(handler => handler.Get<FakeResult>(It.IsAny<string>()), Times.Never());
		}

		[Theory, AutoMoqDataCacheConfig]
		public void CacheDecorator_should_not_call_cache_if_cache_is_diabled_on_query_cache_attribute(
			[Frozen] Mock<ICacheFactory> cacheFactoryMock,
			FakeCacheDisabledQuery query,
			CacheQueryHandlerDecorator<FakeCacheDisabledQuery, FakeResult> sut)
		{
			sut.Handle(query);

			cacheFactoryMock.Verify(handler => handler.GetCache(It.IsAny<CacheType>()), Times.Never());
		}

		[Theory, AutoMoqDataCacheConfig]
		public void CacheDecorator_should_not_call_cache_if_cache_is_globally_disabled(
			[Frozen] Mock<IQueryHandler<FakeQuery, FakeResult>> handlerMock,
			[Frozen] Mock<ICacheConfiguration> cacheConfigMock,
			[Frozen] Mock<ICacheFactory> cacheFactoryMock,
			FakeQuery query,
			CacheQueryHandlerDecorator<FakeQuery, FakeResult> sut)
		{
			cacheConfigMock.Setup(configuration => configuration.Enabled).Returns(false);

			sut.Handle(query);

			cacheFactoryMock.Verify(handler => handler.GetCache(It.IsAny<CacheType>()), Times.Never());
		}

		[Theory, AutoMoqDataCacheConfig]
		public void CacheDecorator_should_not_call_handler_if_cache_returns_non_null_object(
			[Frozen] Mock<ICache> cacheMock,
			[Frozen] Mock<ICacheFactory> cacheFactoryMock,
			[Frozen] Mock<IQueryHandler<FakeCacheQuery, FakeResult>> handlerMock,
			FakeCacheQuery query,
			CacheQueryHandlerDecorator<FakeCacheQuery, FakeResult> sut)
		{
			cacheMock.Setup(cache => cache.CacheType).Returns(() => CacheType.Memory);
			cacheMock.Setup(cache => cache.Get<FakeResult>(It.IsAny<string>())).Returns(() => new FakeResult());
			cacheFactoryMock.Setup(factory => factory.GetCache(It.IsAny<CacheType>())).Returns(() => cacheMock.Object);

			sut.Handle(query);

			handlerMock.Verify(handler => handler.Handle(query), Times.Never());
		}

		[Theory, AutoMoqDataCacheConfig]
		public void CacheDecorator_should_return_cached_object(
			[Frozen] Mock<ICache> cacheMock,
			[Frozen] Mock<ICacheFactory> cacheFactoryMock,
			[Frozen] Mock<IQueryHandler<FakeCacheQuery, FakeResult>> handlerMock,
			FakeCacheQuery query,
			CacheQueryHandlerDecorator<FakeCacheQuery, FakeResult> sut)
		{
			var expected = new FakeResult();
			cacheMock.Setup(cache => cache.CacheType).Returns(() => CacheType.Memory);
			cacheMock.Setup(cache => cache.Get<FakeResult>(It.IsAny<string>())).Returns(() => expected);
			cacheFactoryMock.Setup(factory => factory.GetCache(It.IsAny<CacheType>())).Returns(() => cacheMock.Object);

			var actual = sut.Handle(query);

			handlerMock.Verify(handler => handler.Handle(query), Times.Never());

			Assert.Equal(expected, actual);
		}

		[Theory, AutoMoqDataCacheConfig]
		public void CacheDecorator_should_cache_handler_result_if_not_null(
			[Frozen] Mock<ICache> cacheMock,
			[Frozen] Mock<ICacheFactory> cacheFactoryMock,
			[Frozen] Mock<IQueryHandler<FakeCacheQuery, FakeResult>> handlerMock,
			FakeCacheQuery query,
			CacheQueryHandlerDecorator<FakeCacheQuery, FakeResult> sut)
		{
			var expected = new FakeResult();
			cacheMock.Setup(cache => cache.CacheType).Returns(() => CacheType.Memory);
			cacheMock.Setup(cache => cache.Get<FakeResult>(It.IsAny<string>())).Returns(() => null);
			cacheFactoryMock.Setup(factory => factory.GetCache(It.IsAny<CacheType>())).Returns(() => cacheMock.Object);
			handlerMock.Setup(handler => handler.Handle(query)).Returns(expected);

			sut.Handle(query);

			cacheMock.Verify(cache => cache.Set(It.IsAny<string>(), expected, It.IsAny<TimeSpan>()), Times.Once());
		}
	}
	
	public class AutoMoqDataCacheConfigAttribute : AutoDataAttribute
	{
		public AutoMoqDataCacheConfigAttribute()
			: base(new Fixture()
				.Customize(new MoqCacheConfigurationCustomization()))
		{
		}
	}
	public class MoqCacheConfigurationCustomization : CompositeCustomization
	{
		public MoqCacheConfigurationCustomization()
			: base(new CacheConfigurationCustomization(),
				new AutoMoqCustomization())
		{

		}

		private class CacheConfigurationCustomization : ICustomization
		{
			public void Customize(IFixture fixture)
			{
				fixture.Customizations.Add(new CacheConfigurationSpecimenBuilder());
			}
		}
	}
	public class CacheConfigurationSpecimenBuilder : ISpecimenBuilder
	{
		public object Create(object request, ISpecimenContext context)
		{
			var pi = request as ParameterInfo;
			if (pi == null)
			{
				return new NoSpecimen(request);
			}
			if (pi.ParameterType != typeof(ICacheConfiguration))
			{
				return new NoSpecimen(request);
			}

			var mockCacheConfig = new Mock<ICacheConfiguration>();
			mockCacheConfig.Setup(configuration => configuration.Enabled).Returns(true);
			mockCacheConfig.Setup(configuration => configuration.Targets).Returns(new CacheTargetCollection());
			mockCacheConfig.Setup(configuration => configuration.Profiles).Returns(new CacheProfileCollection());
			mockCacheConfig.Setup(configuration => configuration.DefaultCacheType).Returns(CacheType.Null);
			mockCacheConfig.Setup(configuration => configuration.DefaultCacheName).Returns("Think.Cache");
			mockCacheConfig.Setup(configuration => configuration.DefaultDuration).Returns(60);
			return mockCacheConfig.Object;
		}
	}
	[Cache(Disabled = true)]
	public class FakeCacheDisabledQuery : IQuery<FakeResult>
	{

	}
	[Cache(CacheType = CacheType.Http)]
	public class FakeCacheQuery : IQuery<FakeResult>
	{

	}
	public class FakeQuery : IQuery<FakeResult>
	{

	}
	public class FakeResult
	{
	}
public class CacheQueryHandlerDecorator<TQuery, TResult> : IQueryHandler<TQuery, TResult> where TResult : class where TQuery : IQuery<TResult>
	{
		private readonly ICacheConfiguration _cacheConfiguration;
		private readonly ICacheFactory _cacheFactory;
		private readonly CacheKeyBuilder _cacheKeyBuilder;
		private readonly IQueryHandler<TQuery, TResult> _handler;

		public CacheQueryHandlerDecorator(IQueryHandler<TQuery, TResult> handler, ICacheConfiguration cacheConfiguration, CacheKeyBuilder cacheKeyBuilder, ICacheFactory cacheFactory)
		{
			_cacheConfiguration = cacheConfiguration;
			_cacheKeyBuilder = cacheKeyBuilder;
			_cacheFactory = cacheFactory;
			_handler = handler;
		}

		public TResult Handle(TQuery query)
		{
			//if caching is disabled leave
			if (!_cacheConfiguration.Enabled)
			{
				return _handler.Handle(query);
			}

			//get the cache settings from the attribute & config:
			var cacheAttribute = GetCacheSettings(query);

			//if no cache attribute set leave
			if (cacheAttribute == null)
			{
				return _handler.Handle(query);
			}

			if (cacheAttribute.Disabled)
			{
				return _handler.Handle(query);
			}

			//if there is no cache provider
			var cache = _cacheFactory.GetCache(cacheAttribute.CacheType);
			if (cache == null || cache.CacheType == CacheType.Null)
			{
				return _handler.Handle(query);
			}

			InstrumentCacheRequest(query);
			var cacheKey = _cacheKeyBuilder.GetCacheKey(query);
			var cachedValue = cache.Get<TResult>(cacheKey);

			if (cachedValue == null)
			{
				InstrumentCacheMiss(query);

				//call intended method
				var result = _handler.Handle(query);

				if (result != null)
				{
					var lifespan = cacheAttribute.Lifespan;
					if (lifespan.TotalSeconds > 0)
					{
						cache.Set(cacheKey, result, lifespan);
					}
					else
					{
						cache.Set(cacheKey, result);
					}
				}

				return result;
			}
			InstrumentCacheHit(query);

			return cachedValue;
		}

		private CacheAttribute GetCacheSettings(TQuery query)
		{
			//get the cache attribute & check if overridden in config:
			var cacheAttribute = query.GetType().GetCustomAttribute<CacheAttribute>();
			if (cacheAttribute != null)
			{
				var cacheKeyPrefix = _cacheKeyBuilder.GetCacheKeyPrefix(query);
				var targetConfig = _cacheConfiguration.Targets[cacheKeyPrefix];
				if (targetConfig != null)
				{
					cacheAttribute.Disabled = !targetConfig.Enabled;
					cacheAttribute.Days = targetConfig.Days;
					cacheAttribute.Hours = targetConfig.Hours;
					cacheAttribute.Minutes = targetConfig.Minutes;
					cacheAttribute.Seconds = targetConfig.Seconds;
					cacheAttribute.CacheType = targetConfig.CacheType;
				}
				else
				{
					//if no target is set try and load the profile from configuration
					if (cacheAttribute.ProfileName.IsNotNullOrEmpty())
					{
						var cacheProfile = _cacheConfiguration.Profiles[cacheAttribute.ProfileName];
						cacheAttribute.Days = cacheProfile.Days;
						cacheAttribute.Hours = cacheProfile.Hours;
						cacheAttribute.Minutes = cacheProfile.Minutes;
						cacheAttribute.Seconds = cacheProfile.Seconds;
						cacheAttribute.CacheType = cacheProfile.CacheType;
					}	
				}

				//if no duration is set get the default global value from configuration
				if (cacheAttribute.NoDurationSet())
				{
					cacheAttribute.Seconds = _cacheConfiguration.DefaultDuration;
				}

				//if no cache type is set get the global default from configuration
				if (cacheAttribute.CacheType == CacheType.Null)
				{
					cacheAttribute.CacheType = _cacheConfiguration.DefaultCacheType;
				}
				return cacheAttribute;
			}

			return null;
		}

		private void InstrumentCacheHit(TQuery query)
		{
			//add instrumentation for cache hit here
		}

		private void InstrumentCacheMiss(TQuery query)
		{
			//add instrumentation for cache miss here
		}

		private void InstrumentCacheRequest(TQuery query)
		{
			//add instrumentation for cache call here
		}
	}
public class CacheKeyBuilder
	{
		private readonly ICacheConfiguration _cacheConfiguration;

		public CacheKeyBuilder(ICacheConfiguration cacheConfiguration)
		{
			_cacheConfiguration = cacheConfiguration;
		}

		public string GetCacheKeyPrefix(object query)
		{
			var cacheKeyPrefix = query.GetType().Name;
			this.Log().DebugFormat("CacheKeyBuilder.GetCacheKeyPrefix - returned {0}", cacheKeyPrefix);
			return cacheKeyPrefix;
		}

		public string GetCacheKey(object query)
		{
			var prefix = GetCacheKeyPrefix(query);
			var key = JsonConvert.SerializeObject(query);
			var hashedKey = HashCacheKey(key);
			if (!_cacheConfiguration.HashPrefixInCacheKey)
			{
				hashedKey = string.Format("{0}_{1}", prefix, hashedKey);
			}
			this.Log().DebugFormat("CacheKeyBuilder.GetCacheKey - returned {0}", hashedKey);
			return hashedKey;
		}

		private static string HashCacheKey(string cacheKey)
		{
			//hash the string as a GUID:
			byte[] hashBytes;
			using (var provider = new MD5CryptoServiceProvider())
			{
				var inputBytes = Encoding.Default.GetBytes(cacheKey);
				hashBytes = provider.ComputeHash(inputBytes);
			}
			return new Guid(hashBytes).ToString();
		}
	}
	
	public interface ICacheFactory
	{
		ICache GetCache(CacheType type);
	}
public class CacheFactory : ICacheFactory
	{
		private readonly IEnumerable<ICache> _caches;
		private readonly ICacheConfiguration _cacheConfiguration;

		public CacheFactory(IEnumerable<ICache> caches, ICacheConfiguration cacheConfiguration)
		{
			_caches = caches;
			_cacheConfiguration = cacheConfiguration;
		}

		public ICache GetCache(CacheType type)
		{
			ICache cache = new NullCache(_cacheConfiguration);
			try
			{
				var match = (from c in _caches
					where c.CacheType == type
					select c).LastOrDefault();
				if (match != null)
				{
					match.Initialise();
					cache = match;
				}
			}
			catch (Exception ex)
			{
				this.Log().WarnFormat("Failed to instantiate cache of type: {0}, using null cache. Exception: {1}", type, ex);
				cache = new NullCache(_cacheConfiguration);
			}
			return cache;
		}
	}
/// <summary>
	///     Base class for <see cref="ICache" /> implementations
	/// </summary>
	public abstract class CacheBase : ICache
	{
		protected readonly ICacheConfiguration _configuration;

		protected CacheBase(ICacheConfiguration configuration)
		{
			_configuration = configuration;
		}

		protected virtual bool ItemsNeedSerializing
		{
			get
			{
				return false;
			}
		}

		public abstract CacheType CacheType { get; }

		public void Initialise()
		{
			try
			{
				InitialiseInternal();
			}
			catch (Exception ex)
			{
				this.Log().Error(string.Format("CacheBase.Initialise - failed, NullCache will be used. CacheName: {0}", _configuration.DefaultCacheName), ex);
			}
		}

		public void Set(string key, object value)
		{
			try
			{
				value = PreProcess(value);
				SetInternal(key, value);
			}
			catch (Exception ex)
			{
				this.Log().WarnFormat("CacheBase.Set - failed, item not cached. Message: {0}", ex.Message);
			}
		}

		void ICache.Set(string key, object value, DateTime expiresAt)
		{
			try
			{
				value = PreProcess(value);
				SetInternal(key, value, expiresAt);
			}
			catch (Exception ex)
			{
				this.Log().WarnFormat("CacheBase.Set - failed, item not cached. Message: {0}", ex.Message);
			}
		}

		public void Set(string key, object value, TimeSpan validFor)
		{
			try
			{
				value = PreProcess(value);
				SetInternal(key, value, validFor);
			}
			catch (Exception ex)
			{
				this.Log().WarnFormat("CacheBase.Set - failed, item not cached. Message: {0}", ex.Message);
			}
		}

		public T Get<T>(string key)
		{
			return (T) Get(typeof (T), key);
		}

		public object Get(Type type, string key)
		{
			object item = null;
			try
			{
				item = GetInternal(key);
			}
			catch (Exception ex)
			{
				this.Log().WarnFormat("CacheBase.Get - failed, item not cached. Message: {0}", ex.Message);
			}
			return PostProcess(type, item);
		}

		public void Remove(string key)
		{
			try
			{
				RemoveInternal(key);
			}
			catch (Exception ex)
			{
				this.Log().WarnFormat("CacheBase.Remove - failed, item not cached. Message: {0}", ex.Message);
			}
		}

		public bool Exists(string key)
		{
			var exists = false;
			try
			{
				exists = ExistsInternal(key);
			}
			catch (Exception ex)
			{
				this.Log().WarnFormat("CacheBase.Exists - failed, item not cached. Message: {0}", ex.Message);
			}
			return exists;
		}

		protected abstract bool ExistsInternal(string key);
		protected abstract object GetInternal(string key);
		protected abstract void InitialiseInternal();
		protected abstract void RemoveInternal(string key);
		protected abstract void SetInternal(string key, object value);
		protected abstract void SetInternal(string key, object value, DateTime expiresAt);
		protected abstract void SetInternal(string key, object value, TimeSpan validFor);

		private object PostProcess(Type type, object value)
		{
			//not doing anything at the moment
			return value;
		}

		private object PreProcess(object value)
		{
			//not doing anything at the moment 
			return value;
		}
	}
[AttributeUsage(AttributeTargets.Class,AllowMultiple = false, Inherited = false)]
	public class CacheAttribute : Attribute
	{
		/// <summary>
		/// The name of the cache profile to be used
		/// </summary>
		public string ProfileName { get; set; }

		/// <summary>
		/// Lifespan of the response in the cache
		/// </summary>
		public TimeSpan Lifespan
		{
			get { return new TimeSpan(Days, Hours, Minutes, Seconds); }
		}

		/// <summary>
		/// Whether caching is enabled
		/// </summary>
		public bool Disabled { get; set; }

		/// <summary>
		/// Days the element to be cached should live in the cache
		/// </summary>
		public int Days { get; set; }

		/// <summary>
		/// Hours the element to be cached should live in the cache
		/// </summary>
		public int Hours { get; set; }

		/// <summary>
		/// Minutes the element to be cached should live in the cache
		/// </summary>
		public int Minutes { get; set; }

		/// <summary>
		/// Seconds the items should live in the cache
		/// </summary>
		public int Seconds { get; set; }

		public bool NoDurationSet()
		{
			return Days == 0 && Hours == 0 && Minutes == 0 && Seconds == 0;
		}

		/// <summary>
		/// The type of cache required for the item
		/// </summary>
		public CacheType CacheType { get; set; }
	}