Stay updated

Let’s see how to manage the caching in distributed scenarios with .NET Core and how to choose the right data repository
Distributed cache in ASP.NET Core
Wednesday, September 25, 2019

In the previous article, I explained how to manage the caching in an ASP.NET Core application, using the in-memory caching. It’s possible to use this type of caching if your application is hosted on a single server. Which are instead the tools that the .NET Core framework makes available to make caching in a distributed scenario, as in-cloud?

IDistributedCache Interface

The framework makes available this interface:

public interface IDistributedCache
    byte[] Get(string key);
    Task <byte[]> GetAsync(string key);
    void Set(string key, byte[] value, DistributedCacheEntryOptions options);
    Task SetAsync(string key, byte[] value, DistributedCacheEntryOptions options);
    void Refresh(string key);
    Task RefreshAsync(string key);
    void Remove(string key);
    Task RemoveAsync(string key);

If you remember the interface used in the previous article (IMemoryCache), you may note some similarity, but even many differences, as:

  1. Async methods
  2. Refresh methods (to update cache keys, without asking for data)
  3. Byte-based and not object-based methods

Microsoft provides 3 implementations so far: one is in-memory and it’s mainly used in the development stage of an application, the second one is on SQL server and the last one is based on REDIS.
To use the one based on REDIS it is necessary to install the NuGet package Microsoft.Extensions.Caching.Redis.

Distributed Memory Cache

This implementation is provided by the framework and save our data in memory. It’s not exactly a distributed cache, because data are saved from the application’s instance on the server where it is located. It may be useful in some scenarios:

  • Development and testing
  • When a single server is used in production and memory consumption isn’t an issue.

Anyway, we will enable our application to use the “real” distributed solution only when necessary (the interface used for saving is the same).
To enable this IDistributedCache implementation you only need to register it in the Startup class as shown below:


Distributed SQL Server Cache

This implementation enables the distributed cache to use a SQL Server database as a storage. There are some configuration steps to make. The first step consists of the creation of tables that will be used to persist data.
There’s a line command tool that makes it easy using a simple instruction

dotnet sql-cache create <connection string > <schema > <table >

that permits to create a table with this structure:

CREATE TABLE [dbo].[CacheTable](
    [Id] [nvarchar](449) NOT NULL,
    [Value] [varbinary](max) NOT NULL,
    [ExpiresAtTime] [datetimeoffset](7) NOT NULL,
    [SlidingExpirationInSeconds] [bigint] NULL,
    [AbsoluteExpiration] [datetimeoffset](7) NULL,
    [Id] ASC
CREATE NONCLUSTERED INDEX [Index_ExpiresAtTime] ON [dbo].[CacheTable]
    [ExpiresAtTime] ASC

The second and last step in the configuration of this implementation consists of the registration in our application. In the Startup class, inside the ConfigureServices method, we add this code block:

services.AddDistributedSqlServerCache(o =>
    o.ConnectionString = Configuration["ConnectionStrings:Default"];
    o.SchemaName = "dbo";
    o.TableName = "Cache";

Distributed Redis Cache

The use of Redis is widespread in distributed caching scenarios. Redis is an in-fast-memory datastore, it is opensource and is of key-value type. It provides response times of less than one millisecond, allowing millions of requests per second for each real-time application in a variety of fields.
If the infrastructure of your application is based on an Azure cloud, the service Azure Redis Cache is available and it can be configured from your subscription.

To use the distributed cache on Redis, the installation of the package Microsoft.Extensions.Caching.Redis is necessary.
The first step is configuring the service in the Startup.class

public void ConfigureServices(IServiceCollection services)
  // Add framework services.
  // ... altri servizi ...
  services.AddDistributedRedisCache(cfg => 
    cfg.Configuration = Configuration.GetConnectionString("redis");

If we are using the Azure Redis Cache we can find the connection string by accessing the section “access keys” of the panel.

How to use IDistributedCache

The use of the interface IDistributedCache is particularly simple: if you have already read my article about the use of the interface IMemoryCache, you will find several points in command. If you want to use it in a controller, you need to inject the instance in the constructor for first, as you can see in the following code:

public class HomeController : Controller
    private readonly IDistributedCache _cache;
    public HomeController(IDistributedCache cache)
        _cache = cache;
    public async Task <IActionResult > Index()
        await _cache.SetStringAsync("TestString", "TestValue");
        var value = _cache.GetString("TestString");
        return View();

If you let this code running and you insert a breakpoint in the last line of the Index method, you will have this result:

We can easily check the duration of the cache using the class DistributedCacheEntryOptions. In the following code, we create an instance, setting the duration on one hour.

public async Task <IActionResult > Index()
    var options = new DistributedCacheEntryOptions
        AbsoluteExpiration = DateTime.Now.AddHours(1)
    await _cache.SetStringAsync("TestString", "TestValue", options);
    var value = _cache.GetString("TestString");
    return View();

Conclusions and recommendations

The decision on which implementation of IDistributedCache to use in our apps varies depending on some factors. The choice between Redis and SQL (I keep the memory implementation out because it is used only for testing and development) should be made basing on which infrastructure is available for you, to your performance requirements and to the experience of your development team. If the team feels at ease with Redis, this would be the best choice. The SQL implementation is still a good solution, but it should be remembered that data recovery will not offer excellent performance, so data to be cached should be chosen carefully.