3
\$\begingroup\$

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?

\$\endgroup\$
2
\$\begingroup\$

I can share with you an example version of redis service such as getting subscriber or accessing to the other databases and also for catching connection issues.

ConfigurationOptions allows us to initiaze service with more redis servers(redis pool), if needed you can easly 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 example serialize/deserializefor 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));

| improve this answer | |
\$\endgroup\$
  • 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 Apr 24 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\$ – ASLIM Apr 24 at 20:41

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.