This is a Redis based ICacheProvider for NHibernate written in C# using StackExchange.Redis.
PM> Install-Package NHibernate.Caches.Redis
msbuild .\build\build.proj
and then look
inside the bin
directory.Configure NHibernate to use the custom cache provider:
<property name="cache.use_second_level_cache">true</property>
<property name="cache.use_query_cache">true</property>
<property name="cache.provider_class">NHibernate.Caches.Redis.RedisCacheProvider,
NHibernate.Caches.Redis</property>
Set the ConnectionMultiplexer
on the RedisCacheProvider
before creating your ISessionFactory
:
// Or use your IoC container to wire this up.
var connectionMultiplexer = ConnectionMultiplexer.Connect("localhost:6379");
RedisCacheProvider.SetConnectionMultiplexer(connectionMultiplexer);
using (var sessionFactory = ...)
{
// ...
}
// When your application exits:
connectionMultiplexer.Dispose();
Check out the NHibernate.Caches.Redis.Sample
project to learn more.
You can customize certain behavior with the RedisCacheProvider.SetOptions(options)
method. For example, you can control how objects are serialized into Redis.
Here is a JSON.NET ICacheSerializer
implementation. Once added to your project, you can then configure the options:
var options = new RedisCacheProviderOptions()
{
Serializer = new NhJsonCacheSerializer()
};
RedisCacheProvider.SetOptions(options);
NOTE: XML-based cache configuration (app.config/web.config) was removed in version 3.0.
Using the CacheConfigurations
option, you can customize each region:
RedisCacheProvider.SetOptions(new RedisCacheProviderOptions()
{
Serializer = new NetDataContractCacheSerializer(),
CacheConfigurations = new[]
{
new RedisCacheConfiguration("BlogPost") { Expiration = TimeSpan.FromSeconds(9) }
}
});
You may require that NHibernate gracefully continue to the database as if it missed the cache when an exception occurs. For example, imagine if you are using NHibernate in a web project and your Redis server is unavailable. You may not want NHibernate to continue to timeout for every NHibernate operation. You could do something similar to this:
public class RequestRecoveryRedisCache : RedisCache
{
public const string SkipNHibernateCacheKey = "__SkipNHibernateCache__";
public RequestRecoveryRedisCache(string regionName, IDictionary<string, string> properties, RedisCacheElement element, ConnectionMultiplexer connectionMultiplexer, RedisCacheProviderOptions options)
: base(regionName, properties, element, connectionMultiplexer, options)
{
}
public override object Get(object key)
{
if (HasFailedForThisHttpRequest()) return null;
return base.Get(key);
}
public override void Put(object key, object value)
{
if (HasFailedForThisHttpRequest()) return;
base.Put(key, value);
}
public override void Remove(object key)
{
if (HasFailedForThisHttpRequest()) return;
base.Remove(key);
}
public override void Clear()
{
if (HasFailedForThisHttpRequest()) return;
base.Clear();
}
public override void Destroy()
{
if (HasFailedForThisHttpRequest()) return;
base.Destroy();
}
public override void Lock(object key)
{
if (HasFailedForThisHttpRequest()) return;
base.Lock(key);
}
public override void Unlock(object key)
{
if (HasFailedForThisHttpRequest()) return;
base.Unlock(key);
}
private bool HasFailedForThisHttpRequest()
{
return HttpContext.Current.Items.Contains(SkipNHibernateCacheKey);
}
}
public class RequestRecoveryRedisCacheProvider : RedisCacheProvider
{
protected override RedisCache BuildCache(string regionName, IDictionary<string, string> properties, RedisCacheElement configElement, ConnectionMultiplexer connectionMultiplexer, RedisCacheProviderOptions options)
{
options.OnException = (e) =>
{
HttpContext.Current.Items[RequestRecoveryRedisCache.SkipNHibernateCacheKey] = true;
};
return new RequestRecoveryRedisCache(regionName, properties, configElement, connectionMultiplexer, options);
}
}
Then, use RequestRecoveryRedisCacheProvider
in your web.config
settings.
If one of your other libraries references StackExchange.Redis.StrongName
, and
you're having trouble building, you can use a build alias on the strongly named
reference
to get things to play nice together.
2.6.12
to support Lua scripts. This also means the
cache keys are more simple: NHibernate-Cache:<region_name>:<key>
.RedisCacheConfiguration
can be created and set on the RedisCacheProviderOptions.CacheConfigurations
option.ILockValueFactory
instead of a Func<string>
.IAcquireLockRetryStrategy
).
The default retry strategy has been changed to use an exponential backoff.RedisCacheConfiguration.AcquireLockTimeout
property. It's then available to the IAcquireLockRetryStrategy
.RedisCacheConfiguration.SlidingExpiration
property. By default, no
sliding expiration occurs.RedisCacheExceptionEventArgs
to ExceptionEventArgs
and convert
OnException
to an event: RedisCacheProviderOptions.Exception
.ExceptionEventArgs
.LockFailed
and UnlockFailed
events to RedisCacheProviderOptions
for
handling when locking/unlocking fails (other than exceptions).RedisCacheProvider.SetOptions
(so that, for example, you don't
need to subclass to override OnException
).ICacheSerializer
and setting the Serializer
on the options. The default serializer uses the
NetDataContractSerializer
.Database
option.MyApp.Models.Blog
and a region
prefix of v2
would use the key v2:NHibernate-Cache:v2.MyApp.Models.Blog:keys
.
The key is now v2:NHibernate-Cache:MyApp.Models.Blog:keys
.RedisCache
with RedisCacheException
.OnException
method for sub-classing the cache client and handling
exceptions.@MattiasJakobsson and @Tazer for helping switch over to StackExchange.Redis
.
Happy caching!