References

course

What is redis( Remote Dictionary Server)?

- Redis is a No SQL database which works on the concept of key-value pair. - It is a key-value storage engine with lots of functions. - it support 5 data type: strings, hashes, lists, sets and ordered sets. - Regardless of the type, a value is accessed by a key. - the key is stored as a byte array but you'll mostly use a string as a key.

What is Redis used for?

- Redis can function as a NoSQL database or a memory-cache store to improve performance when serving data that is stored in system memory. - redis can used in a seperate server

Why we use Redis server?

- Redis can be used as a cache for the most frequently used data. - We can use it as an alternative to forcing every single API call to hit our database.

Redis executables:

redis-server is the Redis Server itself. redis-sentinel is the Redis Sentinel executable (monitoring and failover). redis-cli is the command line interface utility to talk with Redis. redis-benchmark is used to check Redis performances. redis-check-aof and redis-check-dump are useful in the rare event of corrupted data files.

How to interact with Redis?

Once installed in a server, run the Redis CLI (Command Line Interface) to issue commands to Redis. While working on the CLI tool, your command-line prompt will change to: redis> sample commands: $ redis-cli redis 127.0.0.1:6379> ping PONG redis 127.0.0.1:6379> set mykey somevalue OK redis 127.0.0.1:6379> get mykey "somevalue"

How to connect to the Redis server?

When installed locally: redis-cli.exe -h localhost -p 6379 From a virtual machine on Azure. Remember to enable non-SSL port because the Redis client may not be compatible with SSL redis-cli.exe -h -p 6379 -a

List all keys

redis localhost:6379> keys * 39) "foo:rand:000000000016" 40) "foo:rand:000000000017" 41) "foo:rand:000000000018" 42) "foo:rand:000000000019" 43) "mykey-f9d21bca-48f6-4cf8-b68d-1652b9533633" 44) "mykey-43d5b08a-5b60-4c72-8820-dcb6f6372b4b"

Delete a specific key

redis localhost:6379> del mykey-0001

Delete all keys

redis localhost:6379> flushall

Set a key-value cached item

localhost:6379> set key001 value001 Ok

Get a cached item give the key

localhost:6379> get key001 "value001"

C#:StackExchangegeneral purpose client

Redis has a .NET package which wraps up the underlying TCP/IP communications and gives us a nice IDistributedCache to work with.

Which NUGET package?

- StackExchange.Redis is a managed wrapper which abstracts the low level client server interactions with the Redis server. - This package is completely dependency injection aware.

Distributed caching and its benefit

- Distributed caching is when you want to handle caching outside of your application. - This also can be shared by one or more applications/servers. - Distributed cache is application-specific; i.e., multiple cache providers support distributed caches. - To implement distributed cache, we can use Redis and NCache. - We will see about Redis cache in detail. - A distributed cache can improve the performance and scalability of an ASP.NET Core app, especially when the app is hosted by a cloud service or a server farm.

IDistributedCache interface

- IDistributedCache Interface provides you with the following methods to perform actions on the actual cache GetAsync - Gets the Value from the Cache Server based on the passed key. SetAsync - Accepts a key and Value and sets it to the Cache server RefreshAsyn c - Resets the Sliding Expiration Timer (more about this later in the article) if any. RemoveAsync - Deletes the cache data based on the key.

Framework provided to implement IDistributedCache

Register an implementation of IDistributedCache in Startup.ConfigureServices. Framework-provided implementations described in this topic include Distributed Memory Cache Distributed SQL Server cache Distributed Redis cache Distributed NCache cache

Setting Up Redis on windows in local machine

1. Run Redis-server.exe (Download from Github repo) 2. Minimize the Redis-server.exe and open Redis-cli.exe. 3. To test, just enter the command ping. 4.Go to the path and open up Powershell and run the following command By default, Redis runs on the local 6379 port. To change the port number, ./redis-server --port {your_port} 5.Once Redis is running at your defined port, the Redis CLI will no longer work. This is because it tries to connect to 6379 by default. To override this, open up PowerShell again and enter the following command, ./redis-cli -p {your_port}

How to create an instance of IDistributedCache without Dependency Injection

This snippet demonstrates creating an instance of IDistributedCache via explicit construction, given the host and port number. This is a very simple example and I would recommend following the DI route    [TestMethod]         public void BasicCreation()         {             try             {                 string server = "localhost";                 string port = "6379";                 string cnstring = $"{server}:{port}";                 var redisOptions = new RedisCacheOptions                 {                     ConfigurationOptions = new ConfigurationOptions()                 };                 redisOptions.ConfigurationOptions.EndPoints.Add(cnstring);                 var opts = Options.Create(redisOptions);                 IDistributedCache cache = new Microsoft.Extensions.Caching.StackExchangeRedis.RedisCache(opts);                 string expectedStringData = "Hello world";                 cache.Set("key003", System.Text.Encoding.UTF8.GetBytes(expectedStringData));                 var dataFromCache = cache.Get("key003");                 var actualCachedStringData = System.Text.Encoding.UTF8.GetString(dataFromCache);                 Assert.AreEqual(expectedStringData, actualCachedStringData);             }             catch (Exception ex)             {                 Trace.WriteLine(ex.ToString());                 throw;             }         }

How to create an instance of IDistributedCache via dependency injection?

This snippet demonstrates creating an instance of IDistributedCache via the ServiceCollection DI container, given the host and port number.

[ClassInitialize]
        public static void Init(TestContext context)
        {            
            var builder = new ConfigurationBuilder();
            builder.AddJsonFile("settings.json", optional: false);
            Config = builder.Build();

            ServiceCollection coll = new ServiceCollection();
            coll.AddStackExchangeRedisCache(options =>
            {
                string server = Config["redis-server"];
                string port = Config["redis-port"];
                string cnstring = $"{server}:{port}";
                options.Configuration = cnstring;
            });

            var sprovider = coll.BuildServiceProvider();
            
            //Use sprovder to create an instance of IDistributedCache
            var cache = sprovider.GetService();
        }

How to create an instance the IServer interface for performing management operations?

The IServer interface is useful for managing an instance of Redis cache.

	   //The overridable Configure method of a Azure function Startup class
        public override void Configure(IFunctionsHostBuilder builder)
        {
            var configurationBuilder = new ConfigurationBuilder();
            configurationBuilder.AddEnvironmentVariables();
            IConfiguration config = configurationBuilder.Build();
            builder.Services.AddSingleton(config);
            builder.Services.AddSingleton(provider => new RedisConfiguration
            {
                ConnectionStringTxn = provider.GetRequiredService()["REDISDEMO_CNSTRING"]
            });
            builder.Services.AddSingleton(this.CreateRedisServerCallBack);

        }
        
        private REDIS.IServer CreateRedisServerCallBack(IServiceProvider provider)
        {
            var redisConfig = provider.GetService();
            var cnstringAdmin = redisConfig.ConnectionStringAdmin;
            //You need allowAdmin=true to call methods .FlushDatabase and .Keys()
            //https://stackexchange.github.io/StackExchange.Redis/Basics.html
            var redis = REDIS.ConnectionMultiplexer.Connect(cnstringAdmin);
            var firstEndPoint = redis.GetEndPoints().FirstOrDefault();
            if (firstEndPoint == null)
            {
                throw new ArgumentException("The endpoints collection was empty. Could not get an end point from Redis connection multiplexer.");
            }
            return redis.GetServer(firstEndPoint);
        }
        
        public class RedisConfiguration
        {
            public string ConnectionStringAdmin => $"{this.ConnectionStringTxn},allowAdmin=true";

            public string ConnectionStringTxn { get; internal set; }

            public override string ToString()
            {
                return $"{ConnectionStringTxn}";
            }
        }

IServer or IDistributedCache,which one do we need ?

The interface IDistributedCache will address the requirements if you key-value pair caching is all you need. However, if you want to manage the Redis cache server itself (e.g. flush the cache, enumerate keys, etc.) then the interface IServer is more powerful

How to work with IDistributedCache without having to intall Redis server locally?

The class MemoryDistributedCache provides an inproc implementation of IDistributedCache and very useful during local development and debugging.


[TestMethod]
        public void ExampleTestMethod()
        {
            var expectedData = new byte[] { 100, 200 };

            var opts = Options.Create(new MemoryDistributedCacheOptions());
            IDistributedCache cache = new MemoryDistributedCache(opts);
            cache.Set("key1", expectedData);
            var cachedData = cache.Get("key1");

            Assert.AreEqual(expectedData, cachedData);

            //Use the variable cache as an input to any class which expects IDistributedCache
        }

How to conditionally inject an IDistributedCache implementation of MemoryDistributedCache during local development?

You are using the key-value caching functionality of Redis. You are coding an Azure function (could be an ASP.NET Core Web app) and you want to develop and debug the code. Do you need to install Redis locally? Not neccessary. Through a couple of lines of clever DI, you can “fool” your classes to use the MemoryDistributedCache implementation of the interface IDistributedCache. In this snippet we are checking for the the value of an environment variable and conditionally injecting the desired implementation of IDistributedCache   [TestMethod]         public void Condition_Injection_Of_IDistributedCache()         {             var builder = new ConfigurationBuilder();             builder.AddJsonFile("settings.json", optional: false);             Config = builder.Build();             ServiceCollection coll = new ServiceCollection();             if (System.Environment.GetEnvironmentVariable("localdebug") == "1")             {                 coll.AddDistributedMemoryCache();             }             else             {                 coll.AddStackExchangeRedisCache(options =>                 {                     string server = Config["redis-server"];                     string port = Config["redis-port"];                     string cnstring = $"{server}:{port}";                     options.Configuration = cnstring;                 });             }             var provider = coll.BuildServiceProvider();             var cache = provider.GetService();         }

Steps to integrate Redis cache in ASP.NET core

1.Make sure redis server is running, ./redis-server --port 6000 2.Install the package that helps you communicate with the Redis server Install-Package Microsoft.Extensions.Caching.StackExchangeRedis 3.Configure in application to support Redis cache with a specific port. In startup.cs/ConfigureServices method:
services.AddStackExchangeRedisCache(options =>  
    {  
        options.Configuration = "localhost:6000";  
    }); 
4.Implementing caching code snippet in Get call of WeatherForeCastController,

    [HttpGet]  
    public async Task < IActionResult > Get() {  
        var cacheKey = "weatherList";  
        string serializedCustomerList;  
        List < string > weatherList = new List < string > ();  
        var redisCustomerList = await _distributedCache.GetAsync(cacheKey);  
        if (redisCustomerList != null) {  
            serializedCustomerList = Encoding.UTF8.GetString(redisCustomerList);  
            weatherList = JsonConvert.DeserializeObject < List < string >> (serializedCustomerList);  
        } else {  
            weatherList = GetFromDb();  
            serializedCustomerList = JsonConvert.SerializeObject(weatherList);  
            redisCustomerList = Encoding.UTF8.GetBytes(serializedCustomerList);  
            var options = new DistributedCacheEntryOptions().SetAbsoluteExpiration(DateTime.Now.AddMinutes(10)).SetSlidingExpiration(TimeSpan.FromMinutes(2));  
            await _distributedCache.SetAsync(cacheKey, redisCustomerList, options);  
        }  
        return Ok(weatherList);  
    }     
We check if the key has a value in Redis, then convert it to a weather list and send back the data and if the value does not exist in Redis, then access the database via efcore (in above code value is hardcoded), get the data and set it to Redis. If the key has value then, the data will be stored in Redis as a byte array. We will be converting this array of a string which will convert the string to an object of type, List DistributedCacheEntryOptions, SetAbsoluteExpiration Here you can set the expiration time of the cached object. SetSlidingExpiration This is similar to Absolute Expiration. It expires as a cached object if it not being requested for a defined amount of time period. Note that Sliding Expiration should always be set lower than the absolute expiration