Quick Overview:
Caching plays a major role in optimizing the application performance. But, among the different caching strategies, it becomes difficult to choose the correct one. That’s why this blog provides insight into a cache-aside pattern and a top-notch caching mechanism, helping you understand its workings, use cases, and implementation.

Cache Aside Pattern in .NET Development

In .NET development, one of the major objectives is to improve the application performance. Several mechanisms are available for it. But, the one that is most preferred is a cache-aside pattern, which is among the top caching strategies in the dotnet ecosystem.

In this blog, we are going to provide a brief overview of this caching strategy and its implementation.

Cache Aside Pattern: A Complete Brief

The cache-aside pattern strategy is an on-demand caching technique, which is also known as lazy loading. Like other .NET caching strategies, it’s also used to improve the .NET application performance, as the software doesn’t have to spend additional time communicating with the data store.

Two major approaches are used to implement a cache aside pattern. The first is using the memory cache, or, you can say, the app or server storage. The second is the Redis cache mechanism. It depends on the software size, type, and complexity while choosing the approach.

Furthermore, this cache strategy solves a major problem, as follows.

The major problem with using a cache was the non-updation of data. Due to this, sometimes the software processes outdated information, impacting the output provided to end-users. This was considered a major drawback, especially for real-time applications.

However, with a cache-aside pattern, the problem was solved. It uses read-through, write-behind, and write-through operations to help reference updated data in database and cache storage. It works in the following three steps.

Step 1: The cache checks whether the data is present in its storage or not.

Step 2: If not available, then it retrieves a copy of the item.

Step 3: If the app updates the item, the cache will use a write-through operation and invalidation mechanism to modify details in the data store. As a result, the cache and database remain updated simultaneously.

The Use Cases of Cache Aside Pattern in .NET Development

Some defined use cases by Microsoft help to understand when and when not to use the cache-aside pattern.

When To Use

  • When the application cache is unable to offer native write and read-through operations.
  • When the application doesn’t assume which data will be requested by the users.
  • When there’s unpredicted or dynamic resource demand.

When Not To Use

  • When the data used by the .NET app is static, as it’s primarily used for ensuring the storage of updated data.

A web farm is used for hosting the web application, using cache-side patterns in such scenarios will cause additional client-server dependencies and complexities.

The Pros and Cons of Cache-Side Strategy

In .NET development, cache-aside patterns do come with some pros and cons that you should know before implementing them.

Pros of Cache-Aside Pattern

  • It helps the developers configure cache flexibility, defining the data type to be cached and for how much time.
  • It improves the application resilience, meaning that software can access the data from data stores if the cache fails. Thus, the data availability is maintained.
  • The cache-aside pattern enables the scaling of the cache storage with the growing demands and user needs.
  • It ensures that the latest and updated data is available in the cache, providing accurate output to end users.

Cons of Cache Aside Pattern

  • Developers need to put additional effort into configuring cache invalidation, cache handling, concurrent requests, and eviction policies.
  • There’s a possibility of multiple cache misses if the app frequently updates data.
  • When the cache-aside pattern uses a write-through operation, the latency is increased because both the cache and database are accessed simultaneously for updation.

The Cache-Side Pattern Implementation

Now, let’s have a look at the practical implementation of cache-side patterns in a .NET application. The process provided below can be used as a reference for your enterprise-grade projects. We are going to undergo the implementation of the cache-aside pattern using Redis in an ASP.NET 5 Web API application.

Step 1: Create an interface, naming “ICacheService,” similar to the below code. The cache-aside pattern is implemented in “GetOrSetAsync,” and the “Remove” method is used for invalidation logic.

using System;
using System.Threading.Tasks;

namespace MyTodoAPI.Services
{
    public interface ICacheService
    {
        Task<T> GetOrSetAsync<T>(string cacheKey, int cacheDurationInMin, Func<Task<T>> func);
        Task Remove(string cacheKey);
    }
}

Step 2: Before configuring the Redis cache, use the NuGet package manager to install the below package.

Package: Microsoft.Extensions.Caching.StackExchangeRedis

dotnet add package Microsoft.Extensions.Caching.StackExchangeRedis --version 5.0.1

Step 3: Now, go to the Azure platform and configure the Azure Redis cache service. Further, complete the “ICacheService” interface implementation by using the “RedisCacheService” class.

using System;
using System.Text.Json;
using System.Threading.Tasks;
using Microsoft.Extensions.Caching.Distributed;

namespace MyTodoAPI.Services
{
    public class RedisCacheService : ICacheService
    {
        private readonly IDistributedCache _distributedCache;

        public RedisCacheService(IDistributedCache distributedCache)
        {
            _distributedCache = distributedCache;
        }

        public async Task<T> GetOrSetAsync<T>(string cacheKey, int cacheDurationInMin, Func<Task<T>> func)
        {
            string cachedItem = await _distributedCache.GetStringAsync(cacheKey);

            if(!string.IsNullOrEmpty(cachedItem))
            {
                return JsonSerializer.Deserialize<T>(cachedItem);
            }

            var item = await func();

            if (item != null)
            {
                var cacheEntryOptions = new DistributedCacheEntryOptions()
                                        .SetSlidingExpiration(TimeSpan.FromMinutes(cacheDurationInMin));

                string serializedItem = JsonSerializer.Serialize(item);

                await _distributedCache.SetStringAsync(cacheKey, serializedItem, cacheEntryOptions);
            }

            return item;
        }

        public async Task Remove(string cacheKey)
        {
            await _distributedCache.RemoveAsync(cacheKey);
        }
    }
}

With these three simple steps, you have completed the cache-aside pattern configuration in the .NET application. The “IDistributedCache” interface is built-in, and Redis is utilized as an adapter. But, the method flow is similar, using the “Remove” method for invalidation purposes.

Cache Aside Pattern Configuration in a Default VS Project

For a better understanding of the cache-aside configuration, we’ll be using a default project available in Visual Studio IDE. Anyone can freely access the “WeatherForecast” project.

Once you have generated the project, execute the following procedure.

Step 1: Create a class “WeatherForecastService” and an interface “IWeatherForecastService”.

The code below consists of a “GetWeatherForecasts” method, having an “ICacheService” interface. It enables the app to fetch data from the cache if it’s previously added to it.

Otherwise, the requested data will be added to the cache, and a new summary will be created. In addition, the “Remove” method will be utilized for invalidation purposes.

using System.Collections.Generic;
using System.Threading.Tasks;

namespace MyTodoAPI.Services
{
    public interface IWeatherForecastService
    {
        Task<List<WeatherForecast>> GetWeatherForecasts();
        Task AddNewWeatherSummary(string summary);
    }
}
using System;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace MyTodoAPI.Services
{
    public class WeatherForecastService : IWeatherForecastService
    {
        private readonly ICacheService _cacheService;

        private static List<string> Summaries = new()
        {
            "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
        };

        private const string GetWeatherForecastsCacheKey = "GetWeatherForecasts";
        private const string GetWeatherForecastsCacheDurationInMin = 30;

        public WeatherForecastService(ICacheService cacheService)
        {
            _cacheService = cacheService;
        }

        public async Task<List<WeatherForecast>> GetWeatherForecasts()
        {
            List<WeatherForecast> weatherForecasts = await _cacheService.GetOrSetAsync(cacheKey: GetWeatherForecastsCacheKey,
                                                                                       cacheDurationInMin: GetWeatherForecastsCacheDurationInMin,
                                                                                       func: () =>
            {
                var rng = new Random();
                var weatherForecasts = new List<WeatherForecast>();

                foreach (var item in Summaries)
                {
                    weatherForecasts.Add(new WeatherForecast
                    {
                        Date = DateTime.Now,
                        TemperatureC = rng.Next(-20, 55),
                        Summary = item
                    });
                }

                return Task.FromResult(weatherForecasts);
            });

            return weatherForecasts;
        }

        public Task AddNewWeatherSummary(string summary)
        {
            Summaries.Add(summary);

            _cacheService.Remove(GetWeatherForecastsCacheKey);

            return Task.CompletedTask;
        }
    }
}

Step 2: Lastly, open the startup file and execute the injection operation.

services.AddStackExchangeRedisCache(opt =>
{
    opt.Configuration = Configuration.GetConnectionString("redis:connection_string");
});

services.AddSingleton<ICacheService, RedisCacheService>();
services.AddScoped<IWeatherForecastService, WeatherForecastService>();

With these two steps, the cache-aside pattern is successfully implemented.

Concluding Up

The cache aside pattern is a reliable caching strategy used for dynamic data needs. In a .NET application, it can be implemented within minutes to improve overall performance. You can use the server storage or the Redis cache on the Azure platform for its implementation. However, always ensure that your application falls under the listed use case for the utmost quality results.

Parag Mehta

Verified Expert in Software & Web App Engineering

Parag Mehta, the CEO and Founder of Positiwise Software Pvt Ltd has extensive knowledge of the development niche. He is implementing custom strategies to craft highly-appealing and robust applications for its clients and supporting employees to grow and ace the tasks. He is a consistent learner and always provides the best-in-quality solutions, accelerating productivity.

Related Posts