{"id":91757,"date":"2023-07-03T02:00:31","date_gmt":"2023-07-03T00:00:31","guid":{"rendered":"https:\/\/code-maze.com\/?p=91757"},"modified":"2024-07-10T12:33:21","modified_gmt":"2024-07-10T10:33:21","slug":"csharp-web-application-caching-redis","status":"publish","type":"post","link":"https:\/\/code-maze.com\/csharp-web-application-caching-redis\/","title":{"rendered":"Easy Web Application Caching With Redis in C#"},"content":{"rendered":"<p>Started to improve the scalability of an Italian startup, Redis has emerged as a powerful solution for efficient and fast data storage and retrieval. With Redis, applications can achieve lightning-fast access to frequently used data enabling millions of requests per second. In this article, we will learn more about Redis, one of the most popular open-source engines in present times.<\/p>\n<div style=\"padding: 20px; border-left: 5px color:#dc2323 solid; display: block; margin-bottom: 20px; box-shadow: 1px 1px 5px 0px lightgrey;\">To download the source code for the video, visit our <a href=\"https:\/\/www.patreon.com\/posts\/source-code-how-107797159?src=WebApplicationCachingWithRedis\" target=\"_blank\" rel=\"nofollow noopener\">Patreon page<\/a> (YouTube Patron tier).<\/div>\n<p><strong>We&#8217;ll be using Docker to install and set up the Redis server in this article. So, a basic knowledge of Docker is a prerequisite.<\/strong><\/p>\n<p>However, before we dive into Redis, let&#8217;s take a moment to understand what caching is and its usage.<\/p>\n<hr \/>\r\n<p style=\"text-align: center;\"><strong>VIDEO<\/strong>: How to Improve Performance of Web APIs Using Redis Cache.<\/p>\r\n<p style=\"text-align: center;\"><iframe width=\"560\" height=\"315\" src=https:\/\/www.youtube.com\/embed\/QDFxi7veYps?si=h7gJphaQ-XrOTnF2 frameborder=\"0\" allow=\"accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture\" allowfullscreen=\"allowfullscreen\"><\/iframe><\/p>\r\n<hr \/>\n<h2><a id=\"caching\"><\/a>What Is Caching?<\/h2>\n<p><strong>Caching is the process of storing data in temporary storage (called cache) to improve the speed of data access.<\/strong> We need to make sure that this storage is faster and more easily accessible than the original data source which can be a database or an API.\u00a0<\/p>\n<p>When an application requests data, we first check if the data is present in the cache. If so, we return the cached data. This process saves us a trip to the original data source which might be costly in terms of performance. Even if the data is not present in the cache on the first request, we can store it in the cache when retrieving it from the external data source, thus making the subsequent requests better in performance.<\/p>\n<p>Also, we can add expiration time to the cache storage so that we can be sure about the consistency of the data stored in it.<\/p>\n<p>Now that we understand what caching is and how it works, let&#8217;s take a look at Redis.<\/p>\n<h2><a id=\"redis\"><\/a>What Is Redis?<\/h2>\n<p><strong><a href=\"https:\/\/redis.io\/\" target=\"_blank\" rel=\"nofollow noopener\">Redis<\/a> (Remote Dictionary Server) is an open-source, in-memory key-value store that allows fast data look-up.<\/strong> We use it primarily as an application cache.<\/p>\n<p>When our applications rely on external data sources such as a database or an API, the performance and the latency of the data access are limited by those resources. We have a bottleneck especially while scaling the application.<\/p>\n<p>This is the problem Redis intends to solve. <strong>It combines the best of <a href=\"https:\/\/code-maze.com\/aspnetcore-in-memory-caching\/\" target=\"_blank\" rel=\"noopener\">in-memory caching<\/a>, to increase the performance of data access and reduce the latency, with distributed caching, to provide scalability and resilience through data replication.<\/strong><\/p>\n<h2><a id=\"setup\"><\/a>Redis Setup\u00a0<\/h2>\n<p>To get started with Redis setup, we need to set up <a href=\"https:\/\/docs.docker.com\/desktop\/\" target=\"_blank\" rel=\"nofollow noopener\">Docker<\/a> on our machine.<\/p>\n<p>The first step is to set up a Redis server using the <code>docker<\/code> command in the command prompt:<\/p>\n<p><code class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\">docker run --name redis-cache -p 90:6379 -d redis<\/code><\/p>\n<p>Here, we create a new Docker container and run it. The <code>--name redis-cache<\/code> flag sets the name of this container to <em>&#8220;redis-cache&#8221;<\/em>.<\/p>\n<p>We map the host machine&#8217;s port to the Redis container&#8217;s port with the <code>-p 90:6379<\/code> flag. Here, <em>&#8220;6379&#8221;<\/em> is the default port for Redis that is mapped to port 90 of the host machine. We can select any available port here.<\/p>\n<p>The flag <code>-d<\/code> ensures that the container runs in a detached mode i.e. in the background. Finally <code>redis<\/code> is the name of the Docker image used to create the container. If the Redis image is not already present, Docker will download it.\u00a0<\/p>\n<p>Running the <code>docker ps -a<\/code> command, we can see all the Docker containers:<\/p>\n<p><a href=\"https:\/\/code-maze.com\/wp-content\/uploads\/2023\/06\/CM_Redis_1-1.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-92087\" src=\"https:\/\/code-maze.com\/wp-content\/uploads\/2023\/06\/CM_Redis_1-1.png\" alt=\"redis-cache\" width=\"980\" height=\"62\" srcset=\"https:\/\/code-maze.com\/wp-content\/uploads\/2023\/06\/CM_Redis_1-1.png 980w, https:\/\/code-maze.com\/wp-content\/uploads\/2023\/06\/CM_Redis_1-1-300x19.png 300w, https:\/\/code-maze.com\/wp-content\/uploads\/2023\/06\/CM_Redis_1-1-768x49.png 768w\" sizes=\"auto, (max-width: 980px) 100vw, 980px\" \/><\/a><\/p>\n<p>Now that we are done creating the Redis server, let&#8217;s execute it:<\/p>\n<p><code class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\">docker exec -it redis-cache sh<\/code><\/p>\n<p>This command launches an interactive shell session with our container. Then, we can execute specific commands inside the Redis server.<\/p>\n<p>That&#8217;s it. Now that the Redis server is up and running, let&#8217;s set up our application.<\/p>\n<h2><a id=\"app-setup\"><\/a>C# Application Setup<\/h2>\n<p>We will create a simple ASP.NET Core Web Application with all the default options enabled, so let&#8217;s use the Visual Studio Project wizard or the <code>dotnet new webapp<\/code> to do so.<\/p>\n<p>Now, before we begin modifying our web application, let&#8217;s install a couple of NuGet packages we need to interact with Redis.\u00a0<\/p>\n<p>In the NuGet Package Manager Console, we need to install <code><span class=\"hljs-variable\">Microsoft<\/span><span class=\"hljs-operator\">.<\/span><span class=\"hljs-variable\">Extensions<\/span><span class=\"hljs-operator\">.<\/span><span class=\"hljs-variable\">Caching<\/span><span class=\"hljs-operator\">.<\/span><span class=\"hljs-variable\">StackExchangeRedis<\/span><\/code>:<\/p>\n<p><code class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\">Install-Package Microsoft.Extensions.Caching.StackExchangeRedis<\/code><\/p>\n<p>This package allows us to use caching in the web application. Additionally, we need to install <code>StackExchange.Redis<\/code>:<\/p>\n<p><code class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\">Install-Package StackExchange.Redis<\/code><\/p>\n<p>This is a client library that allows us to interact with Redis.<\/p>\n<h3><a id=\"app-config\"><\/a>Application Configuration<\/h3>\n<p>Now, let&#8217;s add Redis as our caching provider in our application. To do so, we need to modify the <code>Main()<\/code> method in the <code>Program<\/code> class:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"csharp\">builder.Services.AddStackExchangeRedisCache(options =&gt;\r\n{\r\n    options.Configuration = builder.Configuration.GetConnectionString(\"RedisConn\");\r\n    options.InstanceName = \"GamesCatalog_\";\r\n});<\/pre>\n<p>This adds our caching information to the <a href=\"https:\/\/code-maze.com\/dependency-injection-aspnet\/\" target=\"_blank\" rel=\"noopener\">dependency injection<\/a> system of the application. Hence, we can inject the <code>IDistibutedCache<\/code> interface into any of our services.\u00a0<\/p>\n<p><strong>It is important to note that <code>options.InstanceName<\/code> is an optional property that allows us to prepend a custom instance name to all our cache keys.<\/strong> This is important when we have multiple applications using the same Redis database. It allows us to differentiate between the keys from different instances even if they have the same name.<\/p>\n<p>In the <code>options.Configuration<\/code> property, we specify the connection string for the Redis server.<\/p>\n<p>Let&#8217;s create a connection string <em>&#8220;RedisConn&#8221;<\/em> in the <code>appSettings.json<\/code> file:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"json\" data-enlighter-highlight=\"9-11\">{\r\n  \"Logging\": {\r\n    \"LogLevel\": {\r\n      \"Default\": \"Information\",\r\n      \"Microsoft.AspNetCore\": \"Warning\"\r\n    }\r\n  },\r\n  \"AllowedHosts\": \"*\",\r\n  \"ConnectionStrings\": {\r\n    \"RedisConn\": \"localhost:90\"\r\n  }\r\n}<\/pre>\n<p><strong>The port number we specify here (90) must match the port we mapped the Redis server to in the Docker setup.<\/strong><\/p>\n<h3><a id=\"data\"><\/a>Data Setup<\/h3>\n<p>In a real-world scenario, we&#8217;d be using a database such as SQL or any external API as our data source. We&#8217;ll keep it simple in our example and use static JSON data to act as our external data source.<\/p>\n<p>However, let&#8217;s first create our model class <code>Game<\/code>:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"csharp\">public class Game\r\n{\r\n    public int Id { get; set; }\r\n    public string Title { get; set; } = string.Empty;\r\n    public string Genre { get; set; } = string.Empty;\r\n    public string Platform { get; set; } = string.Empty;\r\n    public int ReleaseYear { get; set; }\r\n}<\/pre>\n<p>We&#8217;ll use this class to map the data from a JSON file <code>GamesData.json<\/code>:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"json\">[\r\n  {\r\n    \"Id\": 1,\r\n    \"Title\": \"The Witcher 3: Wild Hunt\",\r\n    \"Genre\": \"Action RPG\",\r\n    \"Platform\": \"PlayStation 4\",\r\n    \"ReleaseYear\": 2015\r\n  },\r\n  {\r\n    \"Id\": 2,\r\n    \"Title\": \"Red Dead Redemption 2\",\r\n    \"Genre\": \"Action Adventure\",\r\n    \"Platform\": \"Xbox One\",\r\n    \"ReleaseYear\": 2018\r\n  }\r\n]<\/pre>\n<p>This file contains data that represents an array of <code>Game<\/code> objects. We need to make sure that the structure of the JSON data aligns with the properties of the <code>Game<\/code> class.<\/p>\n<p>Now that we have our model class and the JSON data file, we can go ahead with mapping the data to the model class.<\/p>\n<p>Let&#8217;s create a service class <code>GamesService<\/code> and add a method to retrieve all <code>Game<\/code> objects present in our data source:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"csharp\">public Game[] LoadGames()\r\n{\r\n    using var streamReader = new StreamReader(\"Data\/GamesData.json\");\r\n    var gamesData = streamReader.ReadToEnd();\r\n\r\n    var games = JsonSerializer.Deserialize&lt;Game[]&gt;(gamesData);\r\n\r\n    return games;\r\n}<\/pre>\n<p>Here, we read the content of the JSON file and then deserialize the JSON data into an array of <code>Game<\/code> objects. Then, we can use this array <code>Game[]<\/code> in the application.<\/p>\n<h3><a id=\"razor\"><\/a>Razor Page Setup<\/h3>\n<p>Now that we have our data, the next step is to create a user interface to present this data to the user.<\/p>\n<p>Let&#8217;s modify the <code>Index.cshtml<\/code> razor page to show a button that when clicked, will populate the UI with game data.<\/p>\n<p>The button click will send a <a href=\"https:\/\/code-maze.com\/net-core-web-development-part6\/\" target=\"_blank\" rel=\"noopener\">POST<\/a> request to our page model:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"csharp\">public class IndexModel : PageModel\r\n{\r\n    private readonly GamesService _gamesService;\r\n\r\n    public Game[]? Games { get; set; }\r\n\r\n    public IndexModel(GamesService gamesService)\r\n    {\r\n        _gamesService = gamesService;\r\n    }\r\n\r\n    public void OnPostListGames()\r\n    {\r\n        Games = _gamesService.LoadGames();\r\n    }\r\n}<\/pre>\n<p>We use the <code>GamesService<\/code> class to populate game data in the <code>Games<\/code> property of the <code>Index.cshtml.cs<\/code> razor page model. Now, we can use this\u00a0 property in the <code>Index.cshtml<\/code> razor page using <code>Model.Games<\/code>:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"html\">@if (Model.Games != null &amp;&amp; Model.Games.Length &gt; 0)\r\n{\r\n    &lt;table class=\"table table-striped table-bordered\"&gt;\r\n        &lt;thead class=\"thead-dark\"&gt;\r\n            &lt;tr&gt;\r\n                &lt;th&gt;Title&lt;\/th&gt;\r\n                &lt;th&gt;Genre&lt;\/th&gt;\r\n                &lt;th&gt;Platform&lt;\/th&gt;\r\n                &lt;th&gt;Release Year&lt;\/th&gt;\r\n            &lt;\/tr&gt;\r\n        &lt;\/thead&gt;\r\n        &lt;tbody&gt;\r\n            @foreach (var game in Model.Games)\r\n            {\r\n                &lt;tr&gt;\r\n                    &lt;td&gt;@game.Title&lt;\/td&gt;\r\n                    &lt;td&gt;@game.Genre&lt;\/td&gt;\r\n                    &lt;td&gt;@game.Platform&lt;\/td&gt;\r\n                    &lt;td&gt;@game.ReleaseYear&lt;\/td&gt;\r\n                &lt;\/tr&gt;\r\n            }\r\n        &lt;\/tbody&gt;\r\n    &lt;\/table&gt;\r\n}<\/pre>\n<p>Here, we create an HTML table with bootstrap CSS classes. We have table headers that represent every property from the <code>Game<\/code> class. Finally, we iterate over the <code>Model.Games<\/code> collection to create a new row for each game and store the appropriate information in the respective columns.<\/p>\n<h2><a id=\"redis-impl\"><\/a>Implement Redis Caching in C#<\/h2>\n<p>So our application is up and running at this point. However, it is still directly communicating with the database (or mimicking to do so in our case) for every request.<\/p>\n<p>We haven&#8217;t implemented caching yet. So next, let&#8217;s fix that.<\/p>\n<h3><a id=\"service\"><\/a>Caching Service<\/h3>\n<p>Let&#8217;s create a <code>RedisCacheService<\/code> class:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"csharp\">public class RedisCacheService\r\n{\r\n    private readonly IDistributedCache? _cache;\r\n\r\n    public RedisCacheService(IDistributedCache cache)\r\n    {\r\n        _cache = cache;\r\n    }\r\n}<\/pre>\n<p>Here, we inject the <code>IDistributedCache<\/code> interface using the <a href=\"https:\/\/code-maze.com\/dependency-injection-aspnet\/\" target=\"_blank\" rel=\"noopener\">dependency injection<\/a> system of ASP.Net Core. Thus, we can integrate Redis as the caching solution.<\/p>\n<p>Now, let&#8217;s create a method to get the cached data:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"csharp\">public T GetCachedData&lt;T&gt;(string key)\r\n{\r\n    var jsonData = _cache.GetString(key);\r\n\r\n    if (jsonData == null)\r\n        return default(T);\r\n\r\n    return JsonSerializer.Deserialize&lt;T&gt;(jsonData);\r\n}<\/pre>\n<p>The <code>GetCachedData()<\/code> method allows us to retrieve cached data of any specified type <code>T<\/code>. We pass a cache key as a parameter to this method. It retrieves the data present in the cache associated with the <code>key<\/code>. If no such data is present, it returns the default value of the specified type <code>T<\/code>.<\/p>\n<p>Next, let&#8217;s create a method to store any data in the cache:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"csharp\">public void SetCachedData&lt;T&gt;(string key, T data, TimeSpan cacheDuration)\r\n{\r\n    var options = new DistributedCacheEntryOptions\r\n    {\r\n        AbsoluteExpirationRelativeToNow = cacheDuration\r\n    };\r\n\r\n    var jsonData = JsonSerializer.Serialize(data);\r\n    _cache.SetString(key, jsonData, options);\r\n}<\/pre>\n<p>In the <code>SetCachedData()<\/code> method we have three parameters, <code>key<\/code>, for the identifier of the cached data, <code>data<\/code>, representing the actual data we want to cache, and <code>cacheDuration<\/code> to specify the duration for which we want to cache the data.<\/p>\n<p><strong>We serialize the data into JSON before storing it in the cache. It remains in the cache for the specified duration after which it expires.<\/strong><\/p>\n<h3><a id=\"using\"><\/a>Use the Caching Service<\/h3>\n<p>Let&#8217;s update the razor page <code>Index.cshtml<\/code> to use our newly created <code>RedisCacheService<\/code>:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"csharp\" data-enlighter-highlight=\"4,7,9,12\">public class IndexModel : PageModel\r\n{\r\n    private readonly GamesService _gamesService;\r\n    private readonly RedisCacheService _cache;\r\n\r\n    public Game[]? Games { get; set; }\r\n    public bool IsFromCache { get; set; } = false;\r\n\r\n    public IndexModel(GamesService gamesService, RedisCacheService cache)\r\n    {\r\n        _gamesService = gamesService;\r\n        _cache = cache;\r\n    }\r\n}<\/pre>\n<p>Before we use the service, we need to inject the <code>RedisCacheService<\/code> into the <code>IndexModel<\/code> class constructor. Additionally, let&#8217;s also introduce a property <code>IsFromCache<\/code>. This will tell us whether the data in the last request was retrieved from the data source or the cache memory.<\/p>\n<p>Also, let&#8217;s modify the <code>OnPostListGames()<\/code> method to utilize the <code>RedisCacheService<\/code>:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"csharp\" data-enlighter-highlight=\"6,11\">public void OnPostListGames()\r\n{\r\n    var instanceId = GetInstanceId();\r\n    var cacheKey = $\"Games_Cache_{instanceId}\";\r\n\r\n    Games = _cache.GetCachedData&lt;Game[]&gt;(cacheKey);\r\n\r\n    if (Games == null)\r\n    {\r\n        Games = _gamesService.LoadGames();\r\n        _cache.SetCachedData(cacheKey, Games, TimeSpan.FromSeconds(60));\r\n        IsFromCache = false;\r\n    }\r\n    else\r\n    {\r\n        IsFromCache = true;\r\n    }\r\n}<\/pre>\n<p>First, we generate a cache key. It is important that we make this key unique.<\/p>\n<p>Next, we check if any data associated with the key is already present in the cache using the <code>GetCachedData()<\/code> method. If so, we assign the data to the <code>Games<\/code> property.<\/p>\n<p>If no data is cached yet, we send a request to our data source using the <code>LoadGames()<\/code> method. Then, we store this data in the cache using the <code>SetCachedData()<\/code> method and associate it with our cache key. Here, we set the expiration time of the cached data to 60 seconds.\u00a0<\/p>\n<p>Additionally, we set the <code>IsFromCache<\/code> property to <code>true<\/code> or <code>false<\/code> depending on where we retrieve the data from. So, with the click of a button, we send a POST request to the page model and load the data either from the cache or the data source.\u00a0<\/p>\n<p>To ensure our cache key is unique, we can utilize <code>HttpContext.Session<\/code> to store an identifier of the current instance:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"csharp\">private string GetInstanceId()\r\n{\r\n    var instanceId = HttpContext.Session.GetString(\"InstanceId\");\r\n\r\n    if (string.IsNullOrEmpty(instanceId))\r\n    {\r\n        instanceId = Guid.NewGuid().ToString();\r\n        HttpContext.Session.SetString(\"InstanceId\", instanceId);\r\n    }\r\n\r\n    return instanceId;\r\n}<\/pre>\n<p>The <code>GetInstanceId()<\/code> method makes sure that even if we launch multiple instances of our application using different web browsers simultaneously, the application assigns a unique id to each of those instances. Hence, the cache for one browser doesn&#8217;t interfere with another.<\/p>\n<h3><a id=\"in-action\"><\/a>Redis Caching in C# In Action<\/h3>\n<p>Let&#8217;s update the <code>Index<\/code> page to provide the user with a message indicating\u00a0where the requested data is actually coming from:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"html\">&lt;div class=\"text-center mt-5\"&gt;\r\n    &lt;h2 class=\"display-4\"&gt;Mission Successful!&lt;\/h2&gt;\r\n    @if (Model.IsFromCache)\r\n    {\r\n        &lt;p class=\"lead\"&gt;Loading games from the cache&lt;\/p&gt;\r\n    }\r\n    else\r\n    {\r\n        &lt;p class=\"lead\"&gt;Loading games from the API&lt;\/p&gt;\r\n    }\r\n&lt;\/div&gt;<\/pre>\n<p>When a user clicks on the button to list all the games, if the application loads the data from the cache, the user will see the message <em>&#8220;Loading games from the cache&#8221;<\/em>. Whereas, if the source is <code>GamesService<\/code>, the user will see the message <em>&#8220;Loading games from the API&#8221;<\/em> along with the list of games.<\/p>\n<p>We can check available data in the Redis database on the command prompt through the interactive shell we launched inside the Redis container earlier:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\">docker exec -it redis-cache sh\r\n# redis-cli<\/pre>\n<p>On running this command, we get a command line interface for Redis. Next, we can scan the database to see any present records:<\/p>\n<p><code class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\">127.0.0.1:6379&gt; scan 0<\/code><\/p>\n<p>At this point, our Redis database is empty:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\">1) \"0\"\r\n2) (empty array)<\/pre>\n<p>Hence, the API will always handle that initial request. However, as soon as we send the first request, we also create an entry in the cache:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\">127.0.0.1:6379&gt; scan 0\r\n1) \"0\"\r\n2) 1) \"GamesCatalog_Games_Cache_b7ddc896-589d-468c-8c00-303b608bf36c\"\r\n   2) \"GamesCatalog_4e91c363-6a1a-3268-9ed8-acf2fc8c9fda\"<\/pre>\n<p>The first key is the key generated to store our game data in the cache.<\/p>\n<p><strong>The session middleware uses our registered cache service to store data. As we have registered Redis as our cache service, it automatically starts using Redis for the storage of <code>Session<\/code> data. This is the second key we see when scanning.<\/strong><\/p>\n<p>We can use the <code>hgetall &lt;key&gt;<\/code> command to see how the cache stores the game data:<\/p>\n<p><a href=\"https:\/\/code-maze.com\/wp-content\/uploads\/2023\/06\/CM_Redis_3-1-1.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-92088\" src=\"https:\/\/code-maze.com\/wp-content\/uploads\/2023\/06\/CM_Redis_3-1-1.png\" alt=\"Redis cache get key value\" width=\"980\" height=\"301\" srcset=\"https:\/\/code-maze.com\/wp-content\/uploads\/2023\/06\/CM_Redis_3-1-1.png 980w, https:\/\/code-maze.com\/wp-content\/uploads\/2023\/06\/CM_Redis_3-1-1-300x92.png 300w, https:\/\/code-maze.com\/wp-content\/uploads\/2023\/06\/CM_Redis_3-1-1-768x236.png 768w\" sizes=\"auto, (max-width: 980px) 100vw, 980px\" \/><\/a><\/p>\n<p>So, for the next 60 seconds, Redis serves the subsequent requests.\u00a0Once the cache expires, the API will again serve the initial request with Redis taking over for the duration of cache expiration.<\/p>\n<h2><a id=\"cache-expiration\"><\/a>Cache Expiration Policies With Redis in C#<\/h2>\n<p>So, we have this fast and efficient Redis cache for our application. However, we need to manage what data to keep and what to discard from it. This is where cache expiration policies come into the picture.<\/p>\n<p>Cache expiration policies work like time limits that we set for each item in our Redis cache. Once the expiration time is up, the item is automatically removed.<\/p>\n<p>Let&#8217;s look at some of the types of cache expiration policies in Redis.<\/p>\n<h3><strong><a id=\"absolute\"><\/a>Absolute Expiration<\/strong>\u00a0<\/h3>\n<p>This allows us to set a specific date and time after which the cached data expires. We can set an absolute expiration using the <code>AbsoluteExpiration<\/code>\u00a0 property:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"csharp\" data-enlighter-highlight=\"5\">public void SetCachedData&lt;T&gt;(string key, T data, TimeSpan cacheDuration)\r\n{\r\n    var options = new DistributedCacheEntryOptions\r\n    {\r\n        AbsoluteExpiration\u00a0=\u00a0DateTime.Now.Add(cacheDuration)\r\n    };\r\n\r\n    var jsonData = JsonSerializer.Serialize(data);\r\n    _cache.SetString(key, jsonData, options);\r\n}<\/pre>\n<p><strong>Here, we specify a point in time as a deadline after which the cached data is no longer valid.\u00a0<\/strong><\/p>\n<h3><strong><a id=\"sliding\"><\/a>Sliding Expiration <\/strong><\/h3>\n<p>When we don&#8217;t want to depend on the age of data in the cache and rather its frequency of use, sliding expiration is useful. We can use the <code>SlidingExpiration<\/code> property to set the sliding expiration:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"csharp\" data-enlighter-highlight=\"6\">public void SetCachedData&lt;T&gt;(string key, T data, TimeSpan cacheDuration)\r\n{\r\n    var options = new DistributedCacheEntryOptions\r\n    {\r\n        AbsoluteExpiration = DateTime.Now.Add(cacheDuration),\r\n        SlidingExpiration = cacheDuration\r\n    };\r\n\r\n    var jsonData = JsonSerializer.Serialize(data);\r\n    _cache.SetString(key, jsonData, options);\r\n}<\/pre>\n<p><strong>Here, the expiration time resets for the same duration each time we access the data within the specified duration.<\/strong> For example, if we have a cache with a sliding expiration of 60 seconds, the expiration time keeps extending by 60 seconds every time we either access or modify the data within the expiration duration.<\/p>\n<h3><strong><a id=\"ttl\"><\/a>Time-to-Live (TTL) Expiration\u00a0<\/strong><\/h3>\n<p>This is similar to absolute expiration. However, instead of setting an absolute time, we use a relative maximum duration till which the data is valid. We can set it using the <code>AbsoluteExpirationRelativeToNow<\/code> property:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"csharp\" data-enlighter-highlight=\"5\">public void SetCachedData&lt;T&gt;(string key, T data, TimeSpan cacheDuration)\r\n{\r\n    var options = new DistributedCacheEntryOptions\r\n    {\r\n        AbsoluteExpirationRelativeToNow = cacheDuration,\r\n        SlidingExpiration = cacheDuration\r\n    };\r\n\r\n    var jsonData = JsonSerializer.Serialize(data);\r\n    _cache.SetString(key, jsonData, options);\r\n}<\/pre>\n<p>Here, we pass a duration of time using the <code>cacheDuration<\/code> parameter. <strong>Once this duration elapses from the time the data is stored in the cache, the data expires.<\/strong><\/p>\n<p><strong>It&#8217;s important to note that if we set both TTL and sliding expirations, the TTL expiration takes precedence.<\/strong> Thus, the data expires after the cache duration elapses and the expiration is not based on its frequency of access.<\/p>\n<h3><a id=\"mnul-remv\"><\/a>Manual Removal of Cache<\/h3>\n<p><strong>If we don&#8217;t specify any expiration policy, the data lives in the cache indefinitely till we manually remove it.<\/strong> This might be useful if we want to persist some data regardless of how old or infrequently used it is. However, this can lead to our cache being filled with unused and outdated data.<\/p>\n<p>Let&#8217;s implement a button to manually remove the cached data in <code>Index.cshtml<\/code>:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"html\">&lt;form method=\"post\"&gt;\r\n    &lt;button type=\"submit\" class=\"btn btn-danger btn-lg\" asp-page-handler=\"RemoveCache\"&gt;\r\n        Remove Cached Data\r\n    &lt;\/button&gt;\r\n&lt;\/form&gt;<\/pre>\n<p>Additionally, we need to add a method in <code>RedisCacheService<\/code> to remove the cache:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"csharp\">public void RemoveCachedData(string key)\r\n{\r\n    _cache.Remove(key);\r\n}<\/pre>\n<p>The method takes the cache key as an argument and removes it from the Redis cache. On click of the <em>&#8220;Remove Cached Data&#8221;<\/em> button, we send a POST request to get the existing key and remove it:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"csharp\">public void OnPostRemoveCache()\r\n{\r\n    var instanceId = GetInstanceId();\r\n    var cacheKey = $\"Games_Cache_{instanceId}\";\r\n\r\n    _cache.RemoveCachedData(cacheKey);\r\n\r\n    OnPostListGames();\r\n}<\/pre>\n<p>This forces the application to load data using the <code>RedisCacheService<\/code> on the next request and the application shows the message <em>&#8220;Loading games from the API&#8221; <\/em>even though the cache expiration time isn&#8217;t elapsed.<\/p>\n<h2><a id=\"best-practices\"><\/a>Best Practices for Redis in C#<\/h2>\n<p>Let&#8217;s explore some of the best practices that can help us make decisions to utilize Redis more efficiently.<\/p>\n<p>One of the most important caching strategies is to select which data to cache. <strong>We should cache parts of our application that are read-heavy and frequently accessed<\/strong>. Data that is updated frequently is not a good choice here as the cache might not hold the most updated data always.<\/p>\n<p>To prevent the cache data from going stale, <strong>it&#8217;s also important to set an appropriate cache expiration duration<\/strong>. This ensures that the data in the cache is up to date and minimizes the need to retrieve data from the original data source. Redis allows us to set the cache expiration using a TTL (time-to-live) key. However, we need to strike a balance here. A short cache expiration will ensure data freshness but might do so at the cost of higher cache misses.<\/p>\n<p>When the underlying data is updated, <strong>the cache should also reflect the same and should get rid of the previous outdated data<\/strong>. We call this process <a href=\"https:\/\/redis.com\/glossary\/cache-invalidation\/\" target=\"_blank\" rel=\"nofollow noopener\">cache invalidation<\/a>, and it&#8217;s important to keep the cache consistent. This ensures the application doesn&#8217;t serve outdated data to the users.<\/p>\n<p>Finally, similar to the application, caching is a growing process. We need to keep refining our caching strategies based on how the data and the application scale.<\/p>\n<h2><a id=\"conclusion\"><\/a>Conclusion\u00a0<\/h2>\n<p>In this article, we learned about caching and how to implement it using Redis in C#. We looked at how to configure Redis for a .NET web application and also learned about Redis cache expiration policies and some of the best practices for Redis caching.\u00a0<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Started to improve the scalability of an Italian startup, Redis has emerged as a powerful solution for efficient and fast data storage and retrieval. With Redis, applications can achieve lightning-fast access to frequently used data enabling millions of requests per second. In this article, we will learn more about Redis, one of the most popular [&hellip;]<\/p>\n","protected":false},"author":6,"featured_media":62189,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_et_pb_use_builder":"","_et_pb_old_content":"","_et_gb_content_width":"","footnotes":""},"categories":[1677,2081],"tags":[79,1811,1247,1854,1223,45],"class_list":["post-91757","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-caching","category-client-library","tag-asp-net-core","tag-c","tag-caching","tag-redis","tag-redis-cache","tag-web-development","et-has-post-format-content","et_post_format-et-post-format-standard"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v24.7 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Easy Web Application Caching With Redis in C# - Code Maze<\/title>\n<meta name=\"description\" content=\"Redis, which stands for Remote Dictionary Server, is an open source, in-memory, key-value store that provides high-speed data look-up.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/code-maze.com\/csharp-web-application-caching-redis\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Easy Web Application Caching With Redis in C# - Code Maze\" \/>\n<meta property=\"og:description\" content=\"Redis, which stands for Remote Dictionary Server, is an open source, in-memory, key-value store that provides high-speed data look-up.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/code-maze.com\/csharp-web-application-caching-redis\/\" \/>\n<meta property=\"og:site_name\" content=\"Code Maze\" \/>\n<meta property=\"article:published_time\" content=\"2023-07-03T00:00:31+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2024-07-10T10:33:21+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/code-maze.com\/wp-content\/uploads\/2021\/12\/social-csharp.png\" \/>\n\t<meta property=\"og:image:width\" content=\"1100\" \/>\n\t<meta property=\"og:image:height\" content=\"620\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"Code Maze\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@https:\/\/twitter.com\/CodeMazeBlog\" \/>\n<meta name=\"twitter:site\" content=\"@CodeMazeBlog\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Code Maze\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"12 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":[\"Article\",\"BlogPosting\"],\"@id\":\"https:\/\/code-maze.com\/csharp-web-application-caching-redis\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/code-maze.com\/csharp-web-application-caching-redis\/\"},\"author\":{\"name\":\"Code Maze\",\"@id\":\"https:\/\/code-maze.com\/#\/schema\/person\/09d29b223012c8e94a68ba62861d0b04\"},\"headline\":\"Easy Web Application Caching With Redis in C#\",\"datePublished\":\"2023-07-03T00:00:31+00:00\",\"dateModified\":\"2024-07-10T10:33:21+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/code-maze.com\/csharp-web-application-caching-redis\/\"},\"wordCount\":2634,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\/\/code-maze.com\/#organization\"},\"image\":{\"@id\":\"https:\/\/code-maze.com\/csharp-web-application-caching-redis\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/code-maze.com\/wp-content\/uploads\/2021\/12\/social-csharp.png\",\"keywords\":[\"asp.net core\",\"C#\",\"Caching\",\"redis\",\"redis cache\",\"web development\"],\"articleSection\":[\"Caching\",\"Client Library\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/code-maze.com\/csharp-web-application-caching-redis\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/code-maze.com\/csharp-web-application-caching-redis\/\",\"url\":\"https:\/\/code-maze.com\/csharp-web-application-caching-redis\/\",\"name\":\"Easy Web Application Caching With Redis in C# - Code Maze\",\"isPartOf\":{\"@id\":\"https:\/\/code-maze.com\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/code-maze.com\/csharp-web-application-caching-redis\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/code-maze.com\/csharp-web-application-caching-redis\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/code-maze.com\/wp-content\/uploads\/2021\/12\/social-csharp.png\",\"datePublished\":\"2023-07-03T00:00:31+00:00\",\"dateModified\":\"2024-07-10T10:33:21+00:00\",\"description\":\"Redis, which stands for Remote Dictionary Server, is an open source, in-memory, key-value store that provides high-speed data look-up.\",\"breadcrumb\":{\"@id\":\"https:\/\/code-maze.com\/csharp-web-application-caching-redis\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/code-maze.com\/csharp-web-application-caching-redis\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/code-maze.com\/csharp-web-application-caching-redis\/#primaryimage\",\"url\":\"https:\/\/code-maze.com\/wp-content\/uploads\/2021\/12\/social-csharp.png\",\"contentUrl\":\"https:\/\/code-maze.com\/wp-content\/uploads\/2021\/12\/social-csharp.png\",\"width\":1100,\"height\":620,\"caption\":\"C# Development\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/code-maze.com\/csharp-web-application-caching-redis\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/code-maze.com\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Easy Web Application Caching With Redis in C#\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/code-maze.com\/#website\",\"url\":\"https:\/\/code-maze.com\/\",\"name\":\"Code Maze\",\"description\":\"Learn. Code. Succeed.\",\"publisher\":{\"@id\":\"https:\/\/code-maze.com\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/code-maze.com\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/code-maze.com\/#organization\",\"name\":\"Code Maze\",\"url\":\"https:\/\/code-maze.com\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/code-maze.com\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/code-maze.com\/wp-content\/uploads\/2020\/01\/Code-Maze-Only-Logo-Transparent-HRez.png\",\"contentUrl\":\"https:\/\/code-maze.com\/wp-content\/uploads\/2020\/01\/Code-Maze-Only-Logo-Transparent-HRez.png\",\"width\":3511,\"height\":3510,\"caption\":\"Code Maze\"},\"image\":{\"@id\":\"https:\/\/code-maze.com\/#\/schema\/logo\/image\/\"},\"sameAs\":[\"https:\/\/x.com\/CodeMazeBlog\"]},{\"@type\":\"Person\",\"@id\":\"https:\/\/code-maze.com\/#\/schema\/person\/09d29b223012c8e94a68ba62861d0b04\",\"name\":\"Code Maze\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/code-maze.com\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/code-maze.com\/wp-content\/uploads\/2020\/01\/Code-Maze-Only-Logo-Transparent-HRez-150x150.png\",\"contentUrl\":\"https:\/\/code-maze.com\/wp-content\/uploads\/2020\/01\/Code-Maze-Only-Logo-Transparent-HRez-150x150.png\",\"caption\":\"Code Maze\"},\"description\":\"This is the standard author on the site. Most articles are published by individual authors, with their profiles, but when several authors have contributed, we publish collectively as a part of this profile.\",\"sameAs\":[\"https:\/\/www.linkedin.com\/company\/codemaze\/\",\"https:\/\/x.com\/https:\/\/twitter.com\/CodeMazeBlog\"],\"url\":\"https:\/\/code-maze.com\/author\/codemazecontributor\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Easy Web Application Caching With Redis in C# - Code Maze","description":"Redis, which stands for Remote Dictionary Server, is an open source, in-memory, key-value store that provides high-speed data look-up.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/code-maze.com\/csharp-web-application-caching-redis\/","og_locale":"en_US","og_type":"article","og_title":"Easy Web Application Caching With Redis in C# - Code Maze","og_description":"Redis, which stands for Remote Dictionary Server, is an open source, in-memory, key-value store that provides high-speed data look-up.","og_url":"https:\/\/code-maze.com\/csharp-web-application-caching-redis\/","og_site_name":"Code Maze","article_published_time":"2023-07-03T00:00:31+00:00","article_modified_time":"2024-07-10T10:33:21+00:00","og_image":[{"width":1100,"height":620,"url":"https:\/\/code-maze.com\/wp-content\/uploads\/2021\/12\/social-csharp.png","type":"image\/png"}],"author":"Code Maze","twitter_card":"summary_large_image","twitter_creator":"@https:\/\/twitter.com\/CodeMazeBlog","twitter_site":"@CodeMazeBlog","twitter_misc":{"Written by":"Code Maze","Est. reading time":"12 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":["Article","BlogPosting"],"@id":"https:\/\/code-maze.com\/csharp-web-application-caching-redis\/#article","isPartOf":{"@id":"https:\/\/code-maze.com\/csharp-web-application-caching-redis\/"},"author":{"name":"Code Maze","@id":"https:\/\/code-maze.com\/#\/schema\/person\/09d29b223012c8e94a68ba62861d0b04"},"headline":"Easy Web Application Caching With Redis in C#","datePublished":"2023-07-03T00:00:31+00:00","dateModified":"2024-07-10T10:33:21+00:00","mainEntityOfPage":{"@id":"https:\/\/code-maze.com\/csharp-web-application-caching-redis\/"},"wordCount":2634,"commentCount":0,"publisher":{"@id":"https:\/\/code-maze.com\/#organization"},"image":{"@id":"https:\/\/code-maze.com\/csharp-web-application-caching-redis\/#primaryimage"},"thumbnailUrl":"https:\/\/code-maze.com\/wp-content\/uploads\/2021\/12\/social-csharp.png","keywords":["asp.net core","C#","Caching","redis","redis cache","web development"],"articleSection":["Caching","Client Library"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/code-maze.com\/csharp-web-application-caching-redis\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/code-maze.com\/csharp-web-application-caching-redis\/","url":"https:\/\/code-maze.com\/csharp-web-application-caching-redis\/","name":"Easy Web Application Caching With Redis in C# - Code Maze","isPartOf":{"@id":"https:\/\/code-maze.com\/#website"},"primaryImageOfPage":{"@id":"https:\/\/code-maze.com\/csharp-web-application-caching-redis\/#primaryimage"},"image":{"@id":"https:\/\/code-maze.com\/csharp-web-application-caching-redis\/#primaryimage"},"thumbnailUrl":"https:\/\/code-maze.com\/wp-content\/uploads\/2021\/12\/social-csharp.png","datePublished":"2023-07-03T00:00:31+00:00","dateModified":"2024-07-10T10:33:21+00:00","description":"Redis, which stands for Remote Dictionary Server, is an open source, in-memory, key-value store that provides high-speed data look-up.","breadcrumb":{"@id":"https:\/\/code-maze.com\/csharp-web-application-caching-redis\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/code-maze.com\/csharp-web-application-caching-redis\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/code-maze.com\/csharp-web-application-caching-redis\/#primaryimage","url":"https:\/\/code-maze.com\/wp-content\/uploads\/2021\/12\/social-csharp.png","contentUrl":"https:\/\/code-maze.com\/wp-content\/uploads\/2021\/12\/social-csharp.png","width":1100,"height":620,"caption":"C# Development"},{"@type":"BreadcrumbList","@id":"https:\/\/code-maze.com\/csharp-web-application-caching-redis\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/code-maze.com\/"},{"@type":"ListItem","position":2,"name":"Easy Web Application Caching With Redis in C#"}]},{"@type":"WebSite","@id":"https:\/\/code-maze.com\/#website","url":"https:\/\/code-maze.com\/","name":"Code Maze","description":"Learn. Code. Succeed.","publisher":{"@id":"https:\/\/code-maze.com\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/code-maze.com\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/code-maze.com\/#organization","name":"Code Maze","url":"https:\/\/code-maze.com\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/code-maze.com\/#\/schema\/logo\/image\/","url":"https:\/\/code-maze.com\/wp-content\/uploads\/2020\/01\/Code-Maze-Only-Logo-Transparent-HRez.png","contentUrl":"https:\/\/code-maze.com\/wp-content\/uploads\/2020\/01\/Code-Maze-Only-Logo-Transparent-HRez.png","width":3511,"height":3510,"caption":"Code Maze"},"image":{"@id":"https:\/\/code-maze.com\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/x.com\/CodeMazeBlog"]},{"@type":"Person","@id":"https:\/\/code-maze.com\/#\/schema\/person\/09d29b223012c8e94a68ba62861d0b04","name":"Code Maze","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/code-maze.com\/#\/schema\/person\/image\/","url":"https:\/\/code-maze.com\/wp-content\/uploads\/2020\/01\/Code-Maze-Only-Logo-Transparent-HRez-150x150.png","contentUrl":"https:\/\/code-maze.com\/wp-content\/uploads\/2020\/01\/Code-Maze-Only-Logo-Transparent-HRez-150x150.png","caption":"Code Maze"},"description":"This is the standard author on the site. Most articles are published by individual authors, with their profiles, but when several authors have contributed, we publish collectively as a part of this profile.","sameAs":["https:\/\/www.linkedin.com\/company\/codemaze\/","https:\/\/x.com\/https:\/\/twitter.com\/CodeMazeBlog"],"url":"https:\/\/code-maze.com\/author\/codemazecontributor\/"}]}},"_links":{"self":[{"href":"https:\/\/code-maze.com\/wp-json\/wp\/v2\/posts\/91757","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/code-maze.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/code-maze.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/code-maze.com\/wp-json\/wp\/v2\/users\/6"}],"replies":[{"embeddable":true,"href":"https:\/\/code-maze.com\/wp-json\/wp\/v2\/comments?post=91757"}],"version-history":[{"count":7,"href":"https:\/\/code-maze.com\/wp-json\/wp\/v2\/posts\/91757\/revisions"}],"predecessor-version":[{"id":92091,"href":"https:\/\/code-maze.com\/wp-json\/wp\/v2\/posts\/91757\/revisions\/92091"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/code-maze.com\/wp-json\/wp\/v2\/media\/62189"}],"wp:attachment":[{"href":"https:\/\/code-maze.com\/wp-json\/wp\/v2\/media?parent=91757"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/code-maze.com\/wp-json\/wp\/v2\/categories?post=91757"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/code-maze.com\/wp-json\/wp\/v2\/tags?post=91757"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}