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();

2.Dependency injection through constructor

private readonly IMemoryCache memoryCache;
public MemoryCacheHelper(IMemoryCache memoryCache)
{
   this.memoryCache = memoryCache;
}

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 GetOrCreateAsync(string cacheKey, Func> valueFactory, int baseExpireSeconds = 60)
{
    ValidateValueType();
    // 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;
}

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

2.Add distributed cache services

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

3.Dependency Inject from constructor

private readonly IDistributedCache distCache;

public DistributedCacheHelper(IDistributedCache distCache)
{
    this.distCache = distCache;
}

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 GetOrCreateAsync(string cacheKey, Func> 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(jsonStr)!;
    }
}

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

builder.Services.AddDistributedMemoryCache();

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.