ASP.Net Core 3.1
Add following packages to your .csproj
<ItemGroup>
  <PackageReference Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="3.1.15" />
  <PackageReference Include="StackExchange.Redis.Extensions.AspNetCore" Version="7.0.1" />
  <PackageReference Include="StackExchange.Redis.Extensions.Core" Version="7.0.1" />
  <PackageReference Include="StackExchange.Redis.Extensions.Newtonsoft" Version="7.0.1" />
</ItemGroup>
In Startup.cs this is how you would be able to register Redis Client ready to be injected in your workflow code.
public class Startup
{
  public void ConfigureServices(IServiceCollection services)
  {
    // ... other registrations
    // Used By : Sample Below : RedisCacheHelperController (Method 1 Only)
    services.AddSingleton<IConnectionMultiplexer>(
            ConnectionMultiplexer.Connect(DbHelper.GetRedisConnectionHost(Options.IsDevDb()))
        );
    // Used By : Sample Below : DistributedCacheController (Method 2 Only)
    services.AddStackExchangeRedisCache(options => 
            options.Configuration = DbHelper.GetRedisConnectionHost(Options.IsDevDb())
        );
    // ... other registrations
  }
}
Note:
DbHelper.GetRedisConnectionHost(Options.IsDevDb()) :>>> is my way to resolve the connection information/string for my Redis instance respective to my environment. You can have your own way here for that or you can hard-code it here if you like to begin with.
Method 1
So, having the above stuffs in place, would be able to inject the Redis IConnectionMultiplexer into your Controllers or Services.
public class RedisCacheHelperController : ControllerBase
{
    private readonly IConnectionMultiplexer multiplexer;
    public RedisCacheHelperController(IConnectionMultiplexer multiplexer)
    {
        this.multiplexer = multiplexer;
    }
}
Here are the helper APIs to demonstrate how can you use the IConnectionMultiplexer.
public class RedisCacheHelperController : ControllerBase
{
    private readonly IConnectionMultiplexer multiplexer;
    public RedisCacheHelperController(IConnectionMultiplexer multiplexer)
    {
        this.multiplexer = multiplexer;
    }
    [HttpGet("{key}")]
    public async Task<IActionResult> GetCacheByKey([FromRoute] string key)
    {
        var responseContent = await multiplexer.GetDatabase().StringGetAsync(key);
        return Content(
           responseContent,
           Constants.ContentTypeHeaderValueJson // "application/json"
       );
    }
    [HttpPost("{key}")]
    public async Task<IActionResult> PostCacheByKey([FromRoute] string key, [FromBody] object data)
    {
        var requestContent = data.Json(); // JsonConvert.SerializeObject(data)
        await multiplexer.GetDatabase().StringSetAsync(key, requestContent);
        return Ok(key);
    }
    [HttpDelete("{key}")]
    public async Task<IActionResult> DeleteCacheByKey([FromRoute] string key)
    {
        await multiplexer.GetDatabase().KeyDeleteAsync(key);
        return Ok(key);
    }
    [HttpGet("CachedKeys")]
    public IActionResult GetListCacheKeys([FromQuery] [DefaultValue("*")] string pattern)
    {
        var keys = multiplexer
            .GetServer(multiplexer
                .GetEndPoints()
                .First())
            .Keys(pattern: pattern ?? "*")
            .Select(x => x.Get());
        return Ok(keys);
    }
   
    // ... could have more Reids supported operations here
}
Now the above is the use-case where you want access Redis Client and do more Redis specific stuffs. The package Microsoft.Extensions.Caching.StackExchangeRedis we included in .csproj above supports Reids to be registered and injected in as a IDistributedCache. The interface IDistributedCache is been defined by the Microsoft and supports basic/common functionalities by the different distributed cache solutions where Redis is one of that.
Meaning if your purpose is limited to set and/or get cache as a key-value pair, you would rather like to do it this way in Method 2 below.
Method 2
public class DistributedCacheController : ControllerBase
{
    private readonly IDistributedCache distributedCache;
    public DistributedCacheController(IDistributedCache distributedCache)
    {
        this.distributedCache = distributedCache;
    }
    
    [HttpPost("{key}")]
    public async Task<IActionResult> PostCacheByKey([FromRoute] string key, [FromBody] object data)
    {
        var requestContent = data.Json(); // JsonConvert.SerializeObject(data)
        await distributedCache.SetStringAsync(key, requestContent);
        return Ok(key);
    }
    [HttpGet("{key}")]
    public async Task<IActionResult> GetCacheByKey([FromRoute] string key)
    {
        var responseContent = await distributedCache.GetStringAsync(key);
        if (!string.IsNullOrEmpty(responseContent))
        {
            return Content(
               responseContent,
               Constants.ContentTypeHeaderValueJson // "application/json"
            );
        }
        return NotFound();
    }
    [HttpDelete("{key}")]
    public async Task<IActionResult> DeleteCacheByKey([FromRoute] string key)
    {
        await distributedCache.RemoveAsync(key);
        return Ok(key);
    }
}