In a previous post, Redis InMemory Cache in ASP.net MVC Core, I discussed the various forms of cache storage applications may use, introduced Redis and provided examples of how you could start implementing and integrating Redis into your ASP.net MVC core web stack

In hindsight, my previous post may appear to paint the picture that Redis is the fabled Golden Hammer and developers may be tempted to introduce this shiny new tool in the belief that it could be solution to all their session and cache woes.

Just like any consultant worth their day rate, Redis comes with the usual “It Depends!” and the dependency is entirely based on objectives, needs and requirements.

As Software Developers, we live in a world that is constantly changing. The programming languages continue to evolve. Methodologies, Tools, frameworks are here today and gone tomorrow. It is often difficult to sort the wheat from the chaff and despite the valiant best efforts software systems often evolve into balls of mud.

One of the driving factors that guide applications to the classic balls mud architecture is Data and the collection, storage and manipulation of data the changing and often conflicting requirements regarding the importance of data.

As a Web Developer, you never get to decide what, when and where data becomes important, it is beyond your control. It lies firmly in the hands of other stakeholders.

As a Software Solution Architect, you need to consider that your developers will need to cater changes in data collection, manipulation, transformation and storage strategies at the whim of the business.

In many software applications, it’s the final result of a Data Transaction is far more important than storing the data used to preform the transaction. In some cases, there may also be regulatory restrictions in what data you can store and duration you’re entitled to store it for.

The Database Landscape

In the not so distant past, most applications have been architected with only one back-end persistence model in mind. I.e. All data was to be stored in some kind of relational database technology e.g. Microsoft SQL Server, Oracle, PostgreSQL or mySQL.

Integration between systems was primarily handled by either sending CSV files or some kind of XML based technology i.e. SOAP web services, Remote Procedure Calls etc.

The evolution to mobile based technology has forced a rethink on database technology and an explosion of Document based and NoSQL databases.

Each document-oriented database implementation differs on the details of the definition, however in general, they all assume documents encapsulate and encode data in some standard formats or encodings.

Encodings in use include XML, YAML, JSON and BSON, as well as binary forms like PDF and .doc, csv etc.

Among the most popular among these database systems includes names like MongoDB, RavenDB, CouchDB, DocumentDB and DynamoDB.

In typical herd mentality that permeates through the software development industry, of course all developers thundered down the path thinking that of course all Relational Database Technologies are dead and now everything now needs to conform to the cult of the NoSQL.

Hybrid Persistence

The average internet application these days consists of a hybrid persistence model, with various with various database technologies introduced to achieve optimal results where needed.

The days or MS SQL or Oracle being the golden hammer for all persistence challenges are gone, but I still don’t think we’ll see them disappear from the over all stack any time soon.

These hybrid persistence stacks present their own unique challenges, the most notable of which is deciding and knowing which technology to use for the optimum solution to problem you’re trying to solve.

What solutions is Redis Suited For?

Redis is not a plain key-value store, it is actually a data structures server, supporting different kinds of values. What this means is that, while in traditional key-value stores you associated string keys to string values, in Redis the value is not limited to a simple string, but can also hold more complex data structures.

Leaderboards

Redis can manage sets in memory, which gives it an advantage here over memcached. Leaderbaords are sets of items ordered just in time and served from in memory to a page. A database is too slow — and it’s all disposable data. Here both the pipelining and the key/values to sorted sets are powerful features.

Voting Systems

Piping to multiple items and ordering sets in memory where a system reads them out and streams results — websockets! — in real time makes implementing this feature simpler and streamlined.

Analytics

One can implement a page clicks and analytics engine on memcached and backed to a database but redis is really good at counting lists and sets of things. Of all of redis’s features, its ability to do key/value to sorted sets is where it exceeds memcached — and counting something like page clicks per sets of pages and then summing those numbers together into analytics which can be pumped via a worker into a bigger analytics engine is one place where the redis choice is the right one.

Shopping Carts

The majority of data in Shopping carts is ephemeral during a transaction session. A user may store items in a cart for an hour or even a day or two before they commit to purchasing. Making use of Redis on these occassions assists in persisting the data until the time of purchase.

And more …

Developers are continuing to explore the possible use cases for Redis beyond these domains due to it’s speed and simplicity of use.

One last thing: regardless of the choice, a caching system is not a database. In much the same way a database should not be used for caching, it stands to reason that a caching system should be used as a database!

Redis In Action

In my example, I will build on the code from Redis InMemory Cache in ASP.net MVC Core and add a very simple voting system using Redis. It’s not going to be too complex but it will provide enough of an example the steps required.

In the example we’ll be making use of the StackExchange.Redis library.

We are going to make use of a Redis HSET to store our values created using a Simple POCO Class. The object is designed to contain information about votes.

C# public class Vote { public int Yes { get; set; } public int No { get; set; } public int Undecided { get; set; } }

We will create a BaseService which will contain some basic reflection capabilities to derive properties from the Generic Type. One interesting aspect of this class is that it Generates a key that will be used as an Identifier within Redis that will be used to store and retrieve your values.

The GenerateKey follows a Redis Naming convention, of using a colon in your identifier key : so your key will end up resembling somekeyname:somedata all in lower case. To learn more about this naming convention I recommend reading An introduction to Redis data types and abstractions.

C# public abstract class BaseService<T> { protected string Name => this.Type.Name; protected PropertyInfo[] Properties => this.Type.GetProperties(); protected Type Type => typeof(T); /// <summary> /// Generates a key for a Redis Entry , follows the Redis Name Convention of inserting a colon : to identify values /// </summary> /// <param name="key">Redis identifier key</param> /// <returns>concatenates the key with the name of the type</returns> protected string GenerateKey(string key) { return string.Concat(key.ToLower(), ":", this.Name.ToLower()); } protected HashEntry[] GenerateHash(T obj) { var props = this.Properties; var hash = new HashEntry[props.Count()]; for (var i = 0; i < props.Count(); i++) hash[i] = new HashEntry(props[i].Name, props[i].GetValue(obj).ToString()); return hash; } protected T MapFromHash(HashEntry[] hash) { var obj = (T)Activator.CreateInstance(this.Type); // new instance of T var props = this.Properties; for (var i = 0; i < props.Count(); i++) { for (var j = 0; j < hash.Count(); j++) { if (props[i].Name == hash[j].Name) { var val = hash[j].Value; var type = props[i].PropertyType; if (type.IsConstructedGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)) if (string.IsNullOrEmpty(val)) { props[i].SetValue(obj, null); } props[i].SetValue(obj, Convert.ChangeType(val, type)); } } } return obj; } }

We’ll create a very simple UI to enable the voting by placing 3 Buttons

To Enable Voting we’ll write simple Controller method, which in turn just simply passes the values to the RedisVoteService .

C# public IActionResult Vote (string value) { var redis = new RedisVoteService<Vote>(this._fact); var theVote = new Vote(); switch (value) { case "Y": theVote.Yes = 1; break; case "N": theVote.No = 1; break; case "U": theVote.Undecided = 1; break; default: break; } redis.Save("RedisVote",theVote); var model = redis.Get("RedisVote"); return this.PartialView("RedisVote", model); }

Once a Vote is cast We’ll load up a results view using AJAX. In the example I have left the results view up so you can continue to click on the buttons as many times as you like to continue casting votes so you can see the interaction with Redis.

Making use of the Redis Desktop Manager you can also view the results directly within Redis.

Summary

Making use of Redis in an application to store information is extremely easy, for ASP.net MVC Developers this is made even easier by utilising the StackExchange.Redis library, which abstracts all interaction with Redis.

In the above example we have configured the storage within Redis to never expire, however it is a trivial process to configure Redis to automatically expire items within the cache after a period of time. This will be the context of my follow up post, in which we will be further exploring both the functionality of Redis and the StackExchange.Redis library.