Redis && .Net Core
Tags: dotnet, redis, csharp, cache
Published: 2020-12-02 12:02:02

Hi .Net lover, in the previous post I wrote about the Ocelot framework so thank you for taking the time to read the post. In this post, I will write about the cache servers, which I love very much and which I use in professional business life.

In the first part, we'll work on dummy data after that writing code and request public web API on .Net Core 5.0

# Let's start with what is a Cache? 

According to Wikipedia, In computing, a cache (/kæʃ/ (About this sound listens) kash,[1] or /ˈkeɪʃ/ kaysh in Australian English[2]) is a hardware or software component that stores data so that future requests for that data can be served faster; the data stored in a cache might be the result of an earlier computation or a copy of data stored elsewhere. [0]

How many types of cache are there?

1-) In Memory Cache
2-) Distributed Cache
3-) OnDemand and Prepopulation Cache 

# About In-Memory Cache:

Where the data is cached within the server’s memory. 

Pros: Reliable, Suits for small to mid-level applications
Cons: If configured incorrectly, it can consume your server’s resources

How to configuration In-Memory Cache on .Net Core API? [1]

![Screen Shot 2020-12-01 at 3.13.42 PM](https://dev-to-uploads.s3.amazonaws.com/i/wx1gsrgkvdbql1dijrqe.png)
 
In this tutorial, I'd like to study on IMemoryCache interface so this is based on **using Microsoft.Extensions.Caching.Memory;**

![Screen Shot 2020-12-02 at 11.15.32 AM](https://dev-to-uploads.s3.amazonaws.com/i/imdi04arwm4gkpiq9uep.png)

Let's create a new ASP.Net Core 5.0 Web API solution in Visual Studio For Mac. After that, just navigate the Startup file and configure it.

![startup](https://dev-to-uploads.s3.amazonaws.com/i/q7fbch0y6h0iyhjgujzl.png)

Create a new controller and call MovieController.cs then injecting the IMemoryCache the constructor.

![di](https://dev-to-uploads.s3.amazonaws.com/i/w83gf086dxrvpa5y06iv.png)

**Folder structure**
![Screen Shot 2020-12-01 at 2.33.06 PM](https://dev-to-uploads.s3.amazonaws.com/i/6tolpivmvsir829w1mhc.png)
 
**Model folder**
![Screen Shot 2020-12-01 at 2.35.38 PM](https://dev-to-uploads.s3.amazonaws.com/i/6rcwx2v6qq4deytljosz.png)

**Repository folder**
![Screen Shot 2020-12-01 at 2.34.30 PM](https://dev-to-uploads.s3.amazonaws.com/i/cbc2iwrc9belzw7b8d5u.png)

**Controller folder**
![Screen Shot 2020-12-01 at 2.37.20 PM](https://dev-to-uploads.s3.amazonaws.com/i/ydsskcyx4t3xtt992l7m.png)

Test case:(First request time **2:39**)

![Screen Shot 2020-12-01 at 2.39.52 PM](https://dev-to-uploads.s3.amazonaws.com/i/6hep4kynrlns4923hktk.png)

Test case:( the Second request after 1 minute later look at the time **2:40**)

![Screen Shot 2020-12-01 at 2.40.18 PM](https://dev-to-uploads.s3.amazonaws.com/i/5u2urwxs9h95jqgkz4fl.png)
 
# Talk about MemoryCacheEntryOptions:

* SlidingExpiration: Gets or sets how long a cache entry can be inactive (e.g. not accessed) before it will be removed. This will not extend the entry lifetime beyond the absolute expiration (if set). [2]

* AbsoluteExpiration: Gets or sets an absolute expiration date for the cache entry. [2]
 
![Screen Shot 2020-12-01 at 3.01.17 PM](https://dev-to-uploads.s3.amazonaws.com/i/jwzu5z09yi3198ekua62.png)
 
# About Distributed Cache: [4] **RE**mote **DI**ctionary **S**erver

![redis](https://dev-to-uploads.s3.amazonaws.com/i/obnig7b0h6x8x1gpmti8.png)
 
Redis is an open-source (BSD licensed), in-memory data structure store, used as a database, cache, and message broker. It supports data structures such as strings, hashes, lists, sets, sorted sets with range queries, bitmaps, hyper logs, geospatial indexes with radius queries, and streams. [4]

Briefly, Redis is a very fast non-relational database. 

Download Type:
* [Exe](https://redis.io/download)
* [Docker](https://hub.docker.com/_/redis)
   * `docker pull redis`
   * `docker run -p 6379:6379 redis`

![Screen Shot 2020-12-02 at 1.13.48 PM](https://dev-to-uploads.s3.amazonaws.com/i/e1hwuma053ljg6djldvj.png)

# How to check Redis is work? (ping >> PONG)

![Screen Shot 2020-12-02 at 1.16.08 PM](https://dev-to-uploads.s3.amazonaws.com/i/44n0fp4fsml4y1z4lknw.png)

# What are the Redis data types?

* String
Strings are the most basic kind of Redis value. Redis Strings are binary safe, this means that a Redis string can contain any kind of data, for instance, a JPEG image or a serialized Ruby object. A String value can be at a max of 512 Megabytes in length. [5]
* List
* Set
* Sorted Set
* Hash

In the upper part, I gave information about both in-memory and Redis and completed the installation. It's time to transfer data to the Redis server we host on docker. I'll receive data via public web API prepared for Rick and Morty and transfer it to the cache server. So here is the Rick and Mort public API. [RickAndMortApi](https://rickandmortyapi.com/)

Go ahead ->

* Update Model Folder
  * Charatacter.cs
  * Location.cs
  * Origin.cs

```
using System.Collections.Generic;

namespace MovieApp.Api.Model
{
    public class Character
    {
        public int id { get; set; }

        public string name { get; set; }

        public string status { get; set; }

        public string species { get; set; }

        public string type { get; set; }

        public string gender { get; set; }

        public string image { get; set; }

        public Origin origin { get; set; }

        public Location location { get; set; }

        public List<string> episode { get; set; }

        public string url { get; set; }

        public string created { get; set; }
    }
}

namespace MovieApp.Api.Model
{
    public class Location
    {
        public string name { get; set; }

        public string url { get; set; }
    }
}

namespace MovieApp.Api.Model
{
    public class Origin
    {
        public string name { get; set; }

        public string url { get; set; }
    }
}

```
 
* Create a service

[Make HTTP requests using IHttpClientFactory in ASP.NET Core](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/http-requests?view=aspnetcore-5.0)

```
using System.Threading.Tasks;

namespace MovieApp.Api.Services
{
    public interface ICharacterService<T> where T:class
    {
        Task<T> GetCharacter(int id);
    }
}

using System.Net.Http;
using System.Threading.Tasks;
using MovieApp.Api.Model;
using Newtonsoft.Json;

namespace MovieApp.Api.Services
{
    /// <summary>
    /// This class retreive data from rick and mort public web api
    /// </summary>
    public class CharacterService : ICharacterService<Character>
    {
        private readonly IHttpClientFactory _factory;
        public CharacterService(IHttpClientFactory factory) => _factory = factory;

        public async Task<Character> GetCharacter(int id)
        {
            var client = _factory.CreateClient("characterApi");

            var jsonResult = await client.GetStringAsync($"/api/character/{id}");

            return JsonConvert.DeserializeObject<Character>(jsonResult);
        }
    }
}
```

Let's add the CharacterService and AddHttpClient on the Startup file.

![Screen Shot 2020-12-02 at 10.58.47 AM](https://dev-to-uploads.s3.amazonaws.com/i/104ad9krorajvvy9zbcm.png)
 
Also please make sure the API endpoint is set.

```
{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*",
  "ApiEndpoint": "https://rickandmortyapi.com/api/character/"
}
```
* Create a controller

![Screen Shot 2020-12-02 at 11.10.04 AM](https://dev-to-uploads.s3.amazonaws.com/i/ttajoa4pgw2bftwgoan8.png)
 
* Test case

![Screen Shot 2020-12-02 at 11.12.06 AM](https://dev-to-uploads.s3.amazonaws.com/i/r8kv9u1u5qk2mf4ixr9q.png)
 

Our project will go to the library with every request and bring the result. I know this data can not be changed for a while. Every thousand requests will reduce the performance of our API. Because of this reason, I should use the cache mechanism. Now let's Implement the IDistributedCache interface after that we'll test it.

* Please make sure the library is installed

![Screen Shot 2020-12-02 at 1.07.45 PM](https://dev-to-uploads.s3.amazonaws.com/i/n0jwddqvfalveigoxcsn.png)

[More Information IDistributedCache Interface](https://docs.microsoft.com/en-us/dotnet/api/microsoft.extensions.caching.distributed.idistributedcache?view=dotnet-plat-ext-5.0)

After that configure it.

```
 services.AddDistributedRedisCache(options => {
                options.Configuration = "localhost";
                options.InstanceName = "MovieAppInstance";
            });
```

Then we inject IMemoryCache to the constructor:

![Screen Shot 2020-12-02 at 12.09.03 PM](https://dev-to-uploads.s3.amazonaws.com/i/9npaer1i3eagdjl2zkuj.png)

# Character Controller
```
using System;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Caching.Distributed;
using MovieApp.Api.Model;
using MovieApp.Api.Services;
using Newtonsoft.Json;

namespace MovieApp.Api.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class CharacterController : ControllerBase
    {
        private readonly ICharacterService<Character> _characterService;
        private readonly IDistributedCache _distributedCache;

        public CharacterController(ICharacterService<Character> characterService,
                                   IDistributedCache distributedCache)
        {
            _characterService = characterService;
            _distributedCache = distributedCache;
        }

        // GET: api/Character/5
        [HttpGet("{id}", Name = "Get")]
        public async Task<Character> Get(int id)
        {
            var character = new Character();
            var serializedCharacter = string.Empty;

            //Redis cache...
            byte[] encodedCharacters = await _distributedCache.GetAsync($"_character_{id}");

            if(encodedCharacters != null)
            {
                serializedCharacter = Encoding.UTF8.GetString(encodedCharacters);
                return JsonConvert.DeserializeObject<Character>(serializedCharacter);
            }

            character = await _characterService.GetCharacter(id);

            serializedCharacter=  JsonConvert.SerializeObject(character);
            encodedCharacters = Encoding.UTF8.GetBytes(serializedCharacter);

            var options = new DistributedCacheEntryOptions()
            {
                  SlidingExpiration = TimeSpan.FromMinutes(10),
                  AbsoluteExpiration = DateTime.Now.AddMinutes(10)
            };

            await _distributedCache.SetAsync($"_character_{id}", encodedCharacters, options);

            return character;
        }
    }
}

```

Looks good let's get testing.

`https://localhost:5001/api/character/1` what does the first request?

1. Checking whether data is available with the specified key 
2. If data is not available request goes to https://rickandmortyapi.com/ by ChacterService. 
3. When a second request is received, the data is on the Redis server and does not go to the API (CharacterService)

# Test Case

I requested 5 times for different data. I'll show you some screenshots with Redis commands.

`ping` response PONG
`KEYS *`
`HGETALL`

![Screen Shot 2020-12-02 at 1.51.23 PM](https://dev-to-uploads.s3.amazonaws.com/i/u7xg6mbzvmltp2whmq5q.png)

![Screen Shot 2020-12-02 at 1.52.07 PM](https://dev-to-uploads.s3.amazonaws.com/i/4g4i8zowb1oikdyrwqn4.png)

Another Postman request results in there.  Let's see that the duration of the first request is longer than the second request.

Character id: 1

![Screen Shot 2020-12-02 at 1.45.04 PM](https://dev-to-uploads.s3.amazonaws.com/i/ceomd1w5jm2y9w9btso6.png)

![Screen Shot 2020-12-02 at 1.45.42 PM](https://dev-to-uploads.s3.amazonaws.com/i/ous21iurp0kgrf7h34w0.png)

Character id: 2

![Screen Shot 2020-12-02 at 1.48.39 PM](https://dev-to-uploads.s3.amazonaws.com/i/1w33oryvz7tp9rxq6p6j.png) 

![Screen Shot 2020-12-02 at 1.49.01 PM](https://dev-to-uploads.s3.amazonaws.com/i/ndyokqlfpia97hjsihug.png)
 

Thank you for reading :) 👋🏻

Source Code: [Github](https://github.com/bburaksseyhan/rickandmorty)

references:

[0][Wiki](https://en.wikipedia.org/wiki/Cache_(computing)) 
[1][Microsoft](https://docs.microsoft.com/en-us/azure/architecture/patterns/cache-aside)
[2][Microsoft](https://docs.microsoft.com/en-us/dotnet/api/microsoft.extensions.caching.distributed.distributedcacheentryoptions.slidingexpiration?view=dotnet-plat-ext-5.0)
[3][Wiki-About Redis](https://en.wikipedia.org/wiki/Redis)
[4][Redis](https://redis.io/)
[5][Redis-DataTypes](https://redis.io/topics/data-types)