Skip to content

Instantly share code, notes, and snippets.

@dfch
Created March 3, 2017 07:32
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dfch/5623b1f9dd36a13b40696f6928f42c72 to your computer and use it in GitHub Desktop.
Save dfch/5623b1f9dd36a13b40696f6928f42c72 to your computer and use it in GitHub Desktop.
CacheManager - Strongly typed caching with System.Runtime.Caching.MemoryCache - https://d-fens.ch/2017/03/02/nobrainer-strongly-typed-caching-with-system-runtime-caching-memorycache/
/**
* Copyright 2017 d-fens GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Collections.Concurrent;
using System.Runtime.Caching;
namespace Net.Appclusive.Core.Cache
{
public class CacheManagerBase
{
protected const char CACHE_KEY_DELIMITER = '.';
protected static readonly object _SyncRoot = new object();
protected static readonly MemoryCache _MemoryCache = MemoryCache.Default;
protected static readonly ConcurrentDictionary<Type, ICacheManagerSettings> _Map =
new ConcurrentDictionary<Type, ICacheManagerSettings>();
}
}
using System.Diagnostics.Contracts;
/**
* Copyright 2017 d-fens GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System.Reflection;
namespace Net.Appclusive.Core.Cache
{
public abstract class CacheManagerSettingsAbsoluteExpirationBase : ICacheManagerSettingsAbsoluteExpiration
{
private const string VIRTUAL_MEMBER_CALL_IN_CONSTRUCTOR = "You may only either override property ExpirationInSeconds or use protected .ctor(long expirationInSeconds).";
private const BindingFlags BINDING_FLAGS = BindingFlags.Public | BindingFlags.Instance | BindingFlags.FlattenHierarchy;
public bool IsSlidingExpiration { get; set; } = false;
public virtual long ExpirationInSeconds { get; set; } = long.MaxValue;
protected CacheManagerSettingsAbsoluteExpirationBase(long expirationInSeconds)
{
var propertyInfo = this.GetType().GetProperty(nameof(ExpirationInSeconds), BINDING_FLAGS);
Contract.Assert(null != propertyInfo);
Contract.Assert(propertyInfo.DeclaringType == typeof(CacheManagerSettingsAbsoluteExpirationBase), VIRTUAL_MEMBER_CALL_IN_CONSTRUCTOR);
// ReSharper disable once VirtualMemberCallInConstructor
ExpirationInSeconds = expirationInSeconds;
}
protected CacheManagerSettingsAbsoluteExpirationBase()
{
// N/A
}
}
}
/**
* Copyright 2017 d-fens GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Diagnostics.Contracts;
using System.Reflection;
using System.Runtime.Caching;
namespace Net.Appclusive.Core.Cache
{
public abstract class CacheManagerSettingsSlidingExpirationBase : ICacheManagerSettingsSlidingExpiration
{
private const string VIRTUAL_MEMBER_CALL_IN_CONSTRUCTOR = "You may only either override property CacheItemPolicy or use protected .ctor(long slidingExpirationInSeconds).";
private const BindingFlags BINDING_FLAGS = BindingFlags.Public | BindingFlags.Instance | BindingFlags.FlattenHierarchy;
protected CacheManagerSettingsSlidingExpirationBase(long slidingExpirationInSeconds)
{
Contract.Requires(0 < slidingExpirationInSeconds);
var propertyInfo = this.GetType().GetProperty(nameof(CacheItemPolicy), BINDING_FLAGS);
Contract.Assert(null != propertyInfo);
Contract.Assert(propertyInfo.DeclaringType == typeof(CacheManagerSettingsSlidingExpirationBase), VIRTUAL_MEMBER_CALL_IN_CONSTRUCTOR);
// ReSharper disable once VirtualMemberCallInConstructor
CacheItemPolicy = new CacheItemPolicy
{
SlidingExpiration = TimeSpan.FromSeconds(slidingExpirationInSeconds),
};
}
protected CacheManagerSettingsSlidingExpirationBase()
{
// N/A
}
public bool IsSlidingExpiration { get; set; } = true;
public virtual CacheItemPolicy CacheItemPolicy { get; set; } = new CacheItemPolicy
{
SlidingExpiration = ObjectCache.NoSlidingExpiration,
};
}
}
/**
* Copyright 2017 d-fens GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Diagnostics.Contracts;
using System.Runtime.Caching;
using System.Runtime.CompilerServices;
namespace Net.Appclusive.Core.Cache
{
public class CacheManager<TSettings> : CacheManagerBase
where TSettings : ICacheManagerSettings
{
// we want this lock only for the current generic class<T>
// ReSharper disable once StaticMemberInGenericType
private static readonly object _syncRoot = new object();
protected CacheManager()
{
// N/A
}
public static bool IsRegistered()
{
return _Map.ContainsKey(typeof(TSettings));
}
public static CacheManager<TSettings> GetInstance()
{
if (_Map.ContainsKey(typeof(TSettings)))
{
return new CacheManager<TSettings>();
}
lock (_SyncRoot)
{
if (_Map.ContainsKey(typeof(TSettings)))
{
return new CacheManager<TSettings>();
}
Contract.Assert(!typeof(TSettings).IsAbstract);
Contract.Assert(!typeof(TSettings).IsInterface);
var settings = IoC.IoC.DefaultContainer.GetInstance<TSettings>();
Contract.Assert
(
settings is ICacheManagerSettingsAbsoluteExpiration
||
settings is ICacheManagerSettingsSlidingExpiration
);
var isCacheSettingsTypeAddedToCache = _Map.TryAdd(typeof(TSettings), settings);
Contract.Assert(isCacheSettingsTypeAddedToCache, typeof(TSettings).FullName);
return new CacheManager<TSettings>();
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected virtual string GetCacheKey<T>(string key)
{
return string.Concat(typeof(TSettings).FullName, CACHE_KEY_DELIMITER, typeof(T).FullName, CACHE_KEY_DELIMITER, key);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected virtual string GetCacheKey<T>(long key)
{
return string.Concat(typeof(TSettings).FullName, CACHE_KEY_DELIMITER, typeof(T).FullName, CACHE_KEY_DELIMITER, key);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected virtual string GetCacheKey<T>(Guid key)
{
return string.Concat(typeof(TSettings).FullName, CACHE_KEY_DELIMITER, typeof(T).FullName, CACHE_KEY_DELIMITER, key);
}
public virtual T Get<T>(long id, bool useLock = false)
{
return GetInternal<T>(GetCacheKey<T>(id), default(Func<T>), typeof(TSettings), useLock);
}
public virtual T Get<T>(long id, Func<T> valueFactory, bool useLock = false)
{
return GetInternal(GetCacheKey<T>(id), valueFactory, typeof(TSettings), useLock);
}
public virtual T Get<T>(string key, bool useLock = false)
{
Contract.Requires(!string.IsNullOrWhiteSpace(key));
return GetInternal<T>(GetCacheKey<T>(key), default(Func<T>), typeof(TSettings), useLock);
}
public virtual T Get<T>(string key, Func<T> valueFactory, bool useLock = false)
{
Contract.Requires(!string.IsNullOrWhiteSpace(key));
return GetInternal(GetCacheKey<T>(key), valueFactory, typeof(TSettings), useLock);
}
public virtual T Get<T>(Guid key, bool useLock = false)
{
return GetInternal<T>(GetCacheKey<T>(key), default(Func<T>), typeof(TSettings), useLock);
}
public virtual T Get<T>(Guid key, Func<T> valueFactory, bool useLock = false)
{
return GetInternal(GetCacheKey<T>(key), valueFactory, typeof(TSettings), useLock);
}
public virtual T Get<T>(Type key, bool useLock = false)
{
Contract.Requires(null != key);
return GetInternal(GetCacheKey<T>(key.FullName), default(Func<T>), typeof(TSettings), false);
}
public virtual T Get<T>(Type key, Func<T> valueFactory, bool useLock = false)
{
Contract.Requires(null != key);
return GetInternal(GetCacheKey<T>(key.FullName), valueFactory, typeof(TSettings), useLock);
}
private static T GetInternal<T>(string cacheKey, Func<T> valueFactory, Type settingsType, bool useLock = false)
{
if (null == valueFactory)
{
return (T) _MemoryCache[cacheKey];
}
T result;
Func<T> internaValueFactory = () =>
{
if (_MemoryCache.Contains(cacheKey))
{
return (T) _MemoryCache[cacheKey];
}
var value = valueFactory();
if (null == value)
{
return default(T);
}
var settings = _Map[settingsType];
if (settings.IsSlidingExpiration)
{
var policy = ((ICacheManagerSettingsSlidingExpiration) settings).CacheItemPolicy;
_MemoryCache.Set(cacheKey, value, policy);
}
else
{
var expirationInSeconds = ((ICacheManagerSettingsAbsoluteExpiration) settings).ExpirationInSeconds;
var expiration = long.MaxValue != expirationInSeconds
? DateTimeOffset.Now.AddSeconds(expirationInSeconds)
: ObjectCache.InfiniteAbsoluteExpiration;
_MemoryCache.Set(cacheKey, value, expiration);
}
return value;
};
if (useLock)
{
lock (_syncRoot)
{
result = internaValueFactory();
}
}
else
{
result = internaValueFactory();
}
return result;
}
public virtual void Set<T>(long key, T value)
{
Contract.Requires(null != value);
SetInternal(GetCacheKey<T>(key), value, typeof(TSettings));
}
public virtual void Set<T>(string key, T value)
{
Contract.Requires(!string.IsNullOrWhiteSpace(key));
Contract.Requires(null != value);
SetInternal(GetCacheKey<T>(key), value, typeof(TSettings));
}
public virtual void Set<T>(Guid key, T value)
{
Contract.Requires(null != value);
SetInternal(GetCacheKey<T>(key), value, typeof(TSettings));
}
public virtual void Set<T>(Type key, T value)
{
Contract.Requires(null != value);
SetInternal(GetCacheKey<T>(key.FullName), value, typeof(TSettings));
}
private static void SetInternal<T>(string cacheKey, T value, Type settingsType)
{
var settings = _Map[settingsType];
if (settings.IsSlidingExpiration)
{
var policy = ((ICacheManagerSettingsSlidingExpiration)settings).CacheItemPolicy;
_MemoryCache.Set(cacheKey, value, policy);
return;
}
var expirationInSeconds = ((ICacheManagerSettingsAbsoluteExpiration) settings).ExpirationInSeconds;
var expiration = long.MaxValue != expirationInSeconds
? DateTimeOffset.Now.AddSeconds(expirationInSeconds)
: ObjectCache.InfiniteAbsoluteExpiration;
_MemoryCache.Set(cacheKey, value, expiration);
}
public virtual T Remove<T>(long key)
{
return RemoveInternal<T>(GetCacheKey<T>(key));
}
public virtual T Remove<T>(string key)
{
Contract.Requires(!string.IsNullOrWhiteSpace(key));
return RemoveInternal<T>(GetCacheKey<T>(key));
}
public virtual T Remove<T>(Guid key)
{
return RemoveInternal<T>(GetCacheKey<T>(key));
}
public virtual T Remove<T>(Type key)
{
return RemoveInternal<T>(GetCacheKey<T>(key.FullName));
}
private static T RemoveInternal<T>(string cacheKey)
{
return (T) _MemoryCache.Remove(cacheKey);
}
}
}
/**
* Copyright 2017 d-fens GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System.Configuration;
using Net.Appclusive.Public.Constants;
namespace Net.Appclusive.Core.Cache
{
public class CacheSettingsConfigurationSection
: ConfigurationSection
{
//<configuration>
//<!-- Configuration section-handler declaration area. -->
// <configSections>
// <section
// name="cacheManagerSettingsConfiguration"
// type="Net.Appclusive.Core.Cache.CacheSettingsConfigurationSection, Net.Appclusive.Core"
// allowLocation="true"
// allowDefinition="Everywhere"
// />
// <!-- Other <section> and <sectionGroup> elements. -->
// </configSections>
// <!-- Configuration section settings area. -->
// <cacheManagerSettingsConfiguration modelCacheManagerSettingsTimeout="60" />
//</configuration>
[ConfigurationProperty(nameof(CacheSettings.defaultCacheManagerSettingsTimeout), DefaultValue = CacheSettings.defaultCacheManagerSettingsTimeout, IsRequired = false)]
public long DefaultCacheManagerSettingsTimeout
{
get { return (long)this[nameof(CacheSettings.defaultCacheManagerSettingsTimeout)]; }
set { this[nameof(CacheSettings.defaultCacheManagerSettingsTimeout)] = value; }
}
[ConfigurationProperty(nameof(CacheSettings.modelCacheManagerSettingsTimeout), DefaultValue = CacheSettings.modelCacheManagerSettingsTimeout, IsRequired = false)]
public long ModelCacheManagerSettingsTimeout
{
get { return (long)this[nameof(CacheSettings.modelCacheManagerSettingsTimeout)]; }
set { this[nameof(CacheSettings.modelCacheManagerSettingsTimeout)] = value; }
}
[ConfigurationProperty(nameof(CacheSettings.behaviourCacheManagerSettingsTimeout), DefaultValue = CacheSettings.behaviourCacheManagerSettingsTimeout, IsRequired = false)]
public long BehaviourCacheManagerSettingsTimeout
{
get { return (long)this[nameof(CacheSettings.behaviourCacheManagerSettingsTimeout)]; }
set { this[nameof(CacheSettings.behaviourCacheManagerSettingsTimeout)] = value; }
}
[ConfigurationProperty(nameof(CacheSettings.tenantCacheManagerSettingsTimeout), DefaultValue = CacheSettings.tenantCacheManagerSettingsTimeout, IsRequired = false)]
public long TenantCacheManagerSettingsTimeout
{
get { return (long)this[nameof(CacheSettings.tenantCacheManagerSettingsTimeout)]; }
set { this[nameof(CacheSettings.tenantCacheManagerSettingsTimeout)] = value; }
}
}
}
/**
* Copyright 2017 d-fens GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace Net.Appclusive.Core.Cache
{
public interface ICacheManagerSettings
{
bool IsSlidingExpiration { get; set; }
}
}
/**
* Copyright 2017 d-fens GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace Net.Appclusive.Core.Cache
{
public interface ICacheManagerSettingsAbsoluteExpiration : ICacheManagerSettings
{
long ExpirationInSeconds { get; set; }
}
}
/**
* Copyright 2017 d-fens GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System.Runtime.Caching;
namespace Net.Appclusive.Core.Cache
{
public interface ICacheManagerSettingsSlidingExpiration : ICacheManagerSettings
{
CacheItemPolicy CacheItemPolicy { get; set; }
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment