DEV Community

Alex
Alex

Posted on

.NET Learning Notes: How to use distributed and in-memory cache

Reference:
https://learn.microsoft.com/en-us/aspnet/core/performance/caching/memory?view=aspnetcore-9.0
https://learn.microsoft.com/en-us/aspnet/core/performance/caching/distributed?view=aspnetcore-9.0#distributed-memory-cache

Thanks for learning code from Zack.Yang

Caching can significantly improve the performance and scalability of an app by reducing the work required to generate content. Caching works best with data that changes infrequently and is expensive to generate. Caching makes a copy of data that can be returned much faster than from the source. Apps should be written and tested to never depend on cached data.

Simple example for In-Memory Cache
1.Add Memory Cache services

builder.Services.AddMemoryCache();
Enter fullscreen mode Exit fullscreen mode

2.Dependency injection through constructor

private readonly IMemoryCache memoryCache;
public MemoryCacheHelper(IMemoryCache memoryCache)
{
   this.memoryCache = memoryCache;
}
Enter fullscreen mode Exit fullscreen mode

3.Invoke related functions

private static void InitCacheEntry(ICacheEntry entry, int baseExpireSeconds)
{
    //过期时间.Random.Shared 是.NET6新增的
    double sec = Random.Shared.NextDouble(baseExpireSeconds, baseExpireSeconds * 2);
    TimeSpan expiration = TimeSpan.FromSeconds(sec);
    entry.AbsoluteExpirationRelativeToNow = expiration;
}

public async Task<TResult?> GetOrCreateAsync<TResult>(string cacheKey, Func<ICacheEntry, Task<TResult?>> valueFactory, int baseExpireSeconds = 60)
{
    ValidateValueType<TResult>();
    // get from memory cache
    if (!memoryCache.TryGetValue(cacheKey, out TResult result))
    {
        using ICacheEntry entry = memoryCache.CreateEntry(cacheKey);
        // build a CacheEntry, this is the data cached in memory
        InitCacheEntry(entry, baseExpireSeconds);
        // get the data and store it in the memory
        result = (await valueFactory(entry))!;
        entry.Value = result;
    }
    return result;
}
Enter fullscreen mode Exit fullscreen mode

A distributed cache is a cache shared by multiple app servers, typically maintained as an external service to the app servers that access it. A distributed cache can improve the performance and scalability of an ASP.NET Core app, especially when the app is hosted by a cloud service or a server farm.
A distributed cache has several advantages over other caching scenarios where cached data is stored on individual app servers.

Simple example for distributed cache (Redis)
1.Install Nuget:

Microsoft.Extensions.Caching.StackExchangeRedis
Enter fullscreen mode Exit fullscreen mode

2.Add distributed cache services

builder.Services.AddStackExchangeRedisCache(options =>
{
    // redis connect string
    options.Configuration = "localhost:6379";
    options.InstanceName = "SelfLearnProject_";
});
Enter fullscreen mode Exit fullscreen mode

3.Dependency Inject from constructor

private readonly IDistributedCache distCache;

public DistributedCacheHelper(IDistributedCache distCache)
{
    this.distCache = distCache;
}
Enter fullscreen mode Exit fullscreen mode

4.Invoke related functions

private static DistributedCacheEntryOptions CreateOptions(int baseExpireSeconds)
{
    //过期时间.Random.Shared 是.NET6新增的
    double sec = Random.Shared.NextDouble(baseExpireSeconds, baseExpireSeconds * 2);
    TimeSpan expiration = TimeSpan.FromSeconds(sec);
    DistributedCacheEntryOptions options = new DistributedCacheEntryOptions();
    options.AbsoluteExpirationRelativeToNow = expiration;
    return options;
}

public async Task<TResult?> GetOrCreateAsync<TResult>(string cacheKey, Func<DistributedCacheEntryOptions, Task<TResult?>> valueFactory, int expireSeconds = 60)
{
    // Get data from distributed cache
    string jsonStr = await distCache.GetStringAsync(cacheKey);
    // Not found in cache
    if (string.IsNullOrEmpty(jsonStr))
    {
        var options = CreateOptions(expireSeconds);
        TResult? result = await valueFactory(options);
        string jsonOfResult = JsonSerializer.Serialize(result,
                    typeof(TResult));
        // Create data and store it in cache, even the data is null, 
        // we also store. to avoid Cache penetration
        await distCache.SetStringAsync(cacheKey, jsonOfResult, options);
        return result;
    }
    else
    {
        // refresh the expiry
        await distCache.RefreshAsync(cacheKey);
        return JsonSerializer.Deserialize<TResult>(jsonStr)!;
    }
}
Enter fullscreen mode Exit fullscreen mode

In test or develop phase, we could use In-memory to mock distributed function. Instead of registering Redis, we can use:

builder.Services.AddDistributedMemoryCache();
Enter fullscreen mode Exit fullscreen mode

Notice: It is not a true distributed cache, but an in-memory cache that only works within a single instance of an application. It does not provide distributed caching across multiple servers or instances like a real distributed cache(e.g. Redis, NCache, or Memcached) would.

Hostinger image

Get n8n VPS hosting 3x cheaper than a cloud solution

Get fast, easy, secure n8n VPS hosting from $4.99/mo at Hostinger. Automate any workflow using a pre-installed n8n application and no-code customization.

Start now

Top comments (0)

A Workflow Copilot. Tailored to You.

Pieces.app image

Our desktop app, with its intelligent copilot, streamlines coding by generating snippets, extracting code from screenshots, and accelerating problem-solving.

Read the docs

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay