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]
In this tutorial, I'd like to study on IMemoryCache interface so this is based on using Microsoft.Extensions.Caching.Memory;
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.
Create a new controller and call MovieController.cs then injecting the IMemoryCache the constructor.
Test case:(First request time 2:39)
Test case:( the Second request after 1 minute later look at the time 2:40)
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]
About Distributed Cache: [4] REmote DIctionary Server
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:
How to check Redis is work? (ping >> PONG)
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
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
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.
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
- Test case
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
More Information IDistributedCache Interface
After that configure it.
services.AddDistributedRedisCache(options => {
options.Configuration = "localhost";
options.InstanceName = "MovieAppInstance";
});
Then we inject IMemoryCache to the constructor:
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?
- Checking whether data is available with the specified key
- If data is not available request goes to https://rickandmortyapi.com/ by ChacterService.
- 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
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
Character id: 2
Thank you for reading :) 👋🏻
Source Code: Github
references:
[0]Wiki
[1]Microsoft
[2]Microsoft
[3]Wiki-About Redis
[4]Redis
[5]Redis-DataTypes
Top comments (0)