I am currently building cache for my application, and I would like to have my redis cache client work the same way you would dependency inject a DbContext.
I am using StackExchange.Redis to manage my redis cache: https://github.com/StackExchange/StackExchange.Redis
I have made it work, but I am not very confident in the way I implemented it.
In my services I have added:
services.AddSingleton<IConnectionMultiplexer(ConnectionMultiplexer
.Connect("localhost:6379,allowAdmin=true"));
services.AddScoped<ICacheClient, CacheClient>();
I could not figure out how to add IDatabase to services.
My CacheClient looks like this:
public class CacheClient : ICacheClient
{
private readonly IConnectionMultiplexer _connectionMultiplexer;
private readonly IDatabase _redisCache;
private readonly ISerializer _serializer;
public CacheClient(IConnectionMultiplexer connectionMultiplexer, ISerializer serializer)
{
_connectionMultiplexer = connectionMultiplexer;
_serializer = serializer;
_redisCache = connectionMultiplexer.GetDatabase();
}
//add/set cache methods removed for the sake of brevity.
}
From here I can use _redisCache to access the database and do operations such as _redisCache.StringSetAsync etc...
Now ICacheClient can be injected into my application and I can use my cache logic in any class. Which is very nice.
Is there anything wrong with this way of implementing my cache client?
1 Answer 1
I can share with you an example version of Redis service such as getting subscribers or accessing the other databases and also for catching connection issues.
ConfigurationOptions
allows us to initialize service with more Redis servers(Redis pool), if needed you can easily change the constructor parameters.
Note that ConnectionMultiplexer.Connect();
is already static
method, so you can initialize service in class when needed as I did below.
In Addition, I assume that you take ISerializer
for the object serialization, if it is, I added an example of serialize
/deserialize
for you.
I hope it helps you.
public class CacheClient : ICacheClient
{
private readonly ConfigurationOptions configuration = null;
private Lazy<IConnectionMultiplexer> _Connection = null;
public CacheClient(string host = "localhost", int port = 6379, bool allowAdmin = false)
{
configuration = new ConfigurationOptions()
{
//for the redis pool so you can extent later if needed
EndPoints = { { host, port }, },
AllowAdmin = allowAdmin,
//Password = "", //to the security for the production
ClientName = "My Redis Client",
ReconnectRetryPolicy = new LinearRetry(5000),
AbortOnConnectFail = false,
};
_Connection = new Lazy<IConnectionMultiplexer>(() =>
{
ConnectionMultiplexer redis = ConnectionMultiplexer.Connect(configuration);
//redis.ErrorMessage += _Connection_ErrorMessage;
//redis.InternalError += _Connection_InternalError;
//redis.ConnectionFailed += _Connection_ConnectionFailed;
//redis.ConnectionRestored += _Connection_ConnectionRestored;
return redis;
});
}
//for the 'GetSubscriber()' and another Databases
public IConnectionMultiplexer Connection { get { return _Connection.Value; } }
//for the default database
public IDatabase Database => Connection.GetDatabase();
public T JsonGet<T>(RedisKey key, CommandFlags flags = CommandFlags.None)
{
RedisValue rv = Database.StringGet(key, flags);
if (!rv.HasValue)
return default;
T rgv = JsonConvert.DeserializeObject<T>(rv);
return rgv;
}
public bool JsonSet(RedisKey key, object value, TimeSpan? expiry = null, When when = When.Always, CommandFlags flags = CommandFlags.None)
{
if (value == null) return false;
return Database.StringSet(key, JsonConvert.SerializeObject(value), expiry, when, flags);
}
private void _Connection_ErrorMessage(object sender, RedisErrorEventArgs e)
{
throw new NotImplementedException();
}
//add/set cache methods removed for the sake of brevity.
}
Adding Service
services.AddSingleton<ICacheClient>(new CacheClient(allowAdmin:true));
-
1\$\begingroup\$ Very nice, I have been looking for something to handle the case when the redis server is not up. Right now a request throws and error if the redis server is down. Id like to have it just fall back to SQL if it is not up. I will try to implement this. \$\endgroup\$Thorbear– Thorbear2020年04月24日 13:18:54 +00:00Commented Apr 24, 2020 at 13:18
-
\$\begingroup\$ i am also using this code and i am updating the code when i have changed it. if you see better solution please share with me. this is my best solution too. \$\endgroup\$Mustafa Salih ASLIM– Mustafa Salih ASLIM2020年04月24日 20:41:21 +00:00Commented Apr 24, 2020 at 20:41
Explore related questions
See similar questions with these tags.