Distributed Cache, Redis and .NET Core

In many apps there is a need to have distributed cache.  For years now, adopting distributed cache on Windows has been a challenge.  Most popular product, Redis, is no longer supported on Windows platform.  The easiest way to get it going on Windows today is Docker for Windows in my opinion.  To do so, first you have to install Docker for Windows.  I am a fan of graphical user interfaces, so I would also recommend installing Kitematic.  Once that is done, run Kitematic, click New and in search box type Redis.  In search results pick Redis (Redis:latest) and click Create.  Now just wait a bit while you are downloading and isntalling Redis.  Eventually, you will see in Kitematic that Redis is running.  Now you can test Redis.  Open command prompt, I use Terminal for Windows.  You can use PowerShell or Command Prompt, either one. 

Type the following and hit Enter

docker exec -it redis sh

At the next prompt type the following and hit Enter

redis-cli

Now type the following and hit Enter

set name John

You should see Ok.  Now type the following and hit Enter.

get name

You should see John, meaning that you were successful in storing and retrieving a cache entry with the key of name and value of John.

Now let’s create new .NET Core Console app.  Run Visual Studio, create new project, pick .NET Core Console App.  Add new NuGet package of Microsoft.Extensions.Caching.Redis.Core.  For testing reason I am just going to create options class.

    public class MyRedisOptions : IOptions<RedisCacheOptions>
    {
        public RedisCacheOptions Value => new RedisCacheOptions
        {
            Configuration = "127.0.0.1:32768",
            InstanceName = "serge"
        };
    }

The main thing you see is the port number.  You can get that from Kitematic.  If you click on Redis image, under IP and ports you will see Access Url, this is what we need.  I would like to store a instance of a class, so I am going to create a person class.

    [Serializable]
    public class Person
    {
        public string Name { get; set; }
        public Person Parent { get; set; }
    }

You will notices that I added Serializable attribute.  This comes for requirement of IDistributedCache interface.  it only operations on byte arrays.  I am going to lean on BinaryFormatter class, new in .NET Code 2.1 to serialize an object into a byte array.  I am going to create a little extensions class for that that I can reuse everywhere.

    public static class IDistributedCacheExtensions
    {
        public static async Task SetAsync<T>(this IDistributedCache cache, string key, T value, DistributedCacheEntryOptions options)
            where T : class, new()
        {
            using (var stream = new MemoryStream())
            {
                new BinaryFormatter().Serialize(stream, value);
                await cache.SetAsync(key, stream.ToArray(), options);
            }
        }

        public static async Task<T> GetAsync<T>(this IDistributedCache cache, string key)
            where T : class, new()
        {
            var data = await cache.GetAsync(key);
            using (var stream = new MemoryStream(data))
            {
                {
                    return (T)new BinaryFormatter().Deserialize(stream);
                }
            }
        }


        public static async Task SetAsync(this IDistributedCache cache, string key, string value, DistributedCacheEntryOptions options)
        {
            await cache.SetAsync(key, Encoding.UTF8.GetBytes(value), options);
        }

        public static async Task<string> GetAsync(this IDistributedCache cache, string key)
        {

            var data = await cache.GetAsync(key);
            if(data != null)
            {
                return Encoding.UTF8.GetString(data);
            }
            return null;
        }
    }

I am ready for the final test in my console app.

    class Program
    {
        public static async Task Main(string[] args)
        {
            RedisCache cache = new RedisCache(new MyRedisOptions());
            var person = new Person
            {
                Name = "Sergey",
                Parent = new Person
                {
                    Name = "John"
                }
            };
            await cache.SetAsync("sergey", person, new DistributedCacheEntryOptions ());

            var p = await cache.GetAsync<Person>("sergey");
            Console.WriteLine(p.Name + " " + p.Parent.Name);

            await cache.SetAsync("random", "some value", new DistributedCacheEntryOptions());
            Console.ReadKey();
        }
    }

As you can see I am creating an instance of RedisCache, new instance of a Person class, then just set and retrieve the value of Person and some random string.

A few of thoughts in conclusion.

  • Did you notice that I used async main?  Console apps now support async entry point!
  • In normal asp.net core app I would not use Redis options directly, instead I would use methods available in Microsoft.Extensions.Caching.Redis.Core to inject and configure Redis as distributed cache.
  • Now anytime I need distributed cache, I would inject IDistributedCache and set or retrieve the data.
  • I can also use Json.Net to serialize or deserialize object.

Thanks and enjoy.

Leave a Reply

Your email address will not be published. Required fields are marked *