#Code Web.Api with Redis Distributed Cache

Massimo Mannoni
6 min readJan 9, 2019

--

High scalable web api service, for storing frequent access objects

Introduction

Working on web development, an aspect we need to consider is the scalability of our application.

A friend of mine once told me : “Massimo, if you need to increase your application performance, you’re better going to clients and asking for budget for “iron” instead of “software” . They will not understand where their money has been spent in a software improvement, but showing a new big server, will give great satisfaction” ;)

It was 2001.

Actually, the iron concept lost a part of its value, because of the convenience, flexibility and facility of using a cloud service. AWS , Azure and others, took away from us the pleasure to “hug” our blinking, cold and noisy servers.

The Context

Our web application has some content or functionality available for authenticated users. We need to store the users information or state into a repository easy to access and able to scale using different servers.

Prerequisites

Asp.Net Core 2.1 , Redis server.

Implementation

Once Redis server has been installed, we can use a client to interact with. Personally I use Redis Desktop Manager available here https://redisdesktop.com/

http://docs.redisdesktop.com/en/latest/quick-start/ for documentation

The Redis server as a traditional db has an ip address and a tcp port used for communications.

Now we are ready to create our project declaring name and location and choosing API a template.

Using Redis in our application we need to add the NuGet packages required:

Microsoft.Extensions.Caching.Redis

that is available through the browse function on NuGet Manager.

The next step is to declare the server information, in order to be able to access your Redis Server. Open the appsetting.json file and add the following strings :

"ConnectionStrings": {
"Redis": "127.0.0.1:6379"
}

where the ip:port refers to the preview installation and modify the startup.cs file ConfigurationService method as below:

public void ConfigureServices(IServiceCollection services)
{
// add redis cache service
services.AddDistributedRedisCache(options => {
options.Configuration = Configuration.GetConnectionString("Redis");
options.InstanceName = "User_";
});
services.AddSession();
services.AddMvc();
}

Basically we added to IServiceCollection Interface, the distributed cache service backed by Redis using AddDistributedRedisCache method and passing with lambda expression the optional values required.

The option.Configuration will retrieve the Redis server information calling the GetConnectionString and passing the string parameter declared in the appsetting.json file.

The InstanceName is used to set a prefix for the instanced object we will store in our cache repository.

At this point our project has linked to the Redis server instance so, the next step, is to create the model class.

In Model directory create the Class file User.cs and, if necessary, we can add all model classes which we want to store in our cache repository. In my case I have Porfolio and Bank models that are required by User model.

The User Model class should appear as below :

using System;
using System.Collections.Generic;
namespace CacheRepository.Models
{
// declare a user class
public class User
{
// Constructor
public User()
{
}
..... // Properties
public enum UserType : byte
{
person,
company
}

.....
public long UserID { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
public string Email { get; set; }
}
}

With the model declared, we can create the Cache class with the purpose to get and save the object instanced into the cache.

We can create another directory, Application, in which we can add a Cache.cs file.

using Microsoft.Extensions.Caching.Distributed;
using System.Threading.Tasks;
using Newtonsoft.Json;
namespace CacheRepository
{
// declare the class
public static class RedisCache
{
// save
public static async Task SetObjectAsync<T>(IDistributedCache cache, string key, T value)
{
await cache.SetStringAsync(key, JsonConvert.SerializeObject(value));
}
// get
public static async Task<T> GetObjectAsync<T>(IDistributedCache cache, string key)
{
var value = await cache.GetStringAsync(key);
return value == null ? default(T) : JsonConvert.DeserializeObject<T>(value);
}
// verify if an object exists
public static async Task<bool> ExistObjectAsync<T>(IDistributedCache cache, string key)
{
var value = await cache.GetStringAsync(key);
return value == null ? false : true;
}
}
}

First of all, we need to import the namespace required, so we have :

Newtonsoft.Json is the library used to serialize and deserialize the objects instanced, into a Json string;

System.Threading.Tasks because of the use of Task-based Asynchronous Pattern for our operations.

Now we can approach the description of our class methods.

SetObjectAsync<T> : is a static generic <T> Task method that saves a serialized object into the cache. The input parameters are the IDistributedCache injected into the controller class, the string key used as “index” and the data, as generic type to store. The use of generic type is fundamental if we want to have a cache repository suitable for all models.

SetStringAsync is a task that represents the asynchronous operation of saving the data.

GetObjectAsync<T>: similar to the method before, this function is a Task<T> able to get through the GetStringAsync Task data that refers to a specify Key value. The return value, deserializes the JSON to the specified .NET type, in our case the User model.

To close our project we need to add a Web API Controller.

The UserController derived from Controller, exposes the public methods that are reachable by URL and HTTP verb (GET, POST…).

namespace CacheRepository.Controllers
{
[Route("api/[controller]")]
public class UserController : Controller
{
....
}
}

At this point we need to inject the cache.

private readonly IDistributedCache _Cache;public UserController(IDistributedCache distributedCache) => _Cache = distributedCache;

Each time the page is loaded and the constructor UserController is called, the cache is injected and becomes accessible using the _Cache instance.

So we can use _Cache as parameter for the RedisCache class we created, that is consumable as showed below.

// GET api/values[HttpGet("{name}")]
public async Task<User> GetUser(string name)
{
User user = new User();
try
{
if (!string.IsNullOrEmpty(name))
{
user = await RedisCache.GetObjectAsync<User>(_Cache, name);
}
}
catch (Exception ex)
{
throw ex;
}
return user;
}

Getting the URL “Server:Port/Api/User/{name}” the GetUser function is called and it’s possible to get the object stored in cache, using the relative RedisCache static function.

RedisCache.GetObjectAsync<User> will return the deserialize user.

Example of a Postman call to our Api

The Redis desktop screenshot shows on the left panel the objects cached with the their keys “User_” + ID and on the right, the json object representation. Yes some data is hidden ;)

We can add a new controller called BankController, to manage the Bank model, so we can appreciate the advantage in flexibility we get using a web api as cache repository.

// GET api/values[HttpGet("{id}")]
public async Task<Bank> GetBankAccount(string id)
{
Bank bank = new Bank();
try
{
if (!string.IsNullOrEmpty(id))
{
bank = await RedisCache.GetObjectAsync<Bank>(_Cache, id);
}
}
catch (Exception ex)
{
throw ex;
}
return bank;
}

--

--