It took me some time to work out an equivalent for .NET 5+ (formerly .NET Core), so here's a starting point.
The old way of caching has gone and been replaced by Microsoft.Extensions.Caching.Memory with IMemoryCache.
I separated it out a bit more, so here's what you need...
The Cache Management Class
I've added the whole thing here, so you can see the using statements.
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Primitives;
using System;
using System.Threading;
namespace MyWebApplication
{
    public interface IThrottleCache
    {
        bool AddToCache(string key, int expriryTimeInSeconds);
        bool AddToCache<T>(string key, T value, int expriryTimeInSeconds);
        T GetFromCache<T>(string key);
        bool IsInCache(string key);
    }
    /// <summary>
    /// A caching class, based on the docs
    /// https://learn.microsoft.com/en-us/aspnet/core/performance/caching/memory?view=aspnetcore-6.0
    /// Uses the recommended library "Microsoft.Extensions.Caching.Memory"
    /// </summary>
    public class ThrottleCache : IThrottleCache
    {
        private IMemoryCache _memoryCache;
        public ThrottleCache(IMemoryCache memoryCache)
        {
            _memoryCache = memoryCache;
        }
        public bool AddToCache(string key, int expriryTimeInSeconds)
        {
            bool isSuccess = false; // Only a success if a new value gets added.
            if (!IsInCache(key))
            {
                var cancellationTokenSource = new CancellationTokenSource(
                                                     TimeSpan.FromSeconds(expriryTimeInSeconds));
                var cacheEntryOptions = new MemoryCacheEntryOptions()
                    .SetSize(1)
                    .AddExpirationToken(
                        new CancellationChangeToken(cancellationTokenSource.Token));
                _memoryCache.Set(key, DateTime.Now, cacheEntryOptions);
                isSuccess = true;
            }
            return isSuccess;
        }
        public bool AddToCache<T>(string key, T value, int expriryTimeInSeconds)
        {
            bool isSuccess = false;
            if (!IsInCache(key))
            {
                var cancellationTokenSource = new CancellationTokenSource(
                                                     TimeSpan.FromSeconds(expriryTimeInSeconds));
                var cacheEntryOptions = new MemoryCacheEntryOptions()
                    .SetAbsoluteExpiration(DateTimeOffset.Now.AddSeconds(expriryTimeInSeconds))
                    .SetSize(1)
                    .AddExpirationToken(
                        new CancellationChangeToken(cancellationTokenSource.Token));
                _memoryCache.Set<T>(key, value, cacheEntryOptions);
                isSuccess = true;
            }
            return isSuccess;
        }
        public T GetFromCache<T>(string key)
        {
            return _memoryCache.Get<T>(key);
        }
        public bool IsInCache(string key)
        {
            var item = _memoryCache.Get(key);
            return item != null;
        }
    }
}
The attribute itself
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using System;
using System.Net;
namespace MyWebApplication
{
    /// <summary>
    /// Decorates any MVC route that needs to have client requests limited by time.
    /// Based on how they throttle at stack overflow (updated for .NET5+)
    /// https://stackoverflow.com/questions/33969/best-way-to-implement-request-throttling-in-asp-net-mvc/1318059#1318059
    /// </summary>
    /// <remarks>
    /// Uses the current System.Web.Caching.Cache to store each client request to the decorated route.
    /// </remarks>
    [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
    public class ThrottleByIPAddressAttribute : ActionFilterAttribute
    {
        /// <summary>
        /// The caching class (which will be instantiated as a singleton)
        /// </summary>
        private IThrottleCache _throttleCache;
        /// <summary>
        /// A unique name for this Throttle.
        /// </summary>
        /// <remarks>
        /// We'll be inserting a Cache record based on this name and client IP, e.g. "Name-192.168.0.1"
        /// </remarks>
        public string Name { get; set; }
        /// <summary>
        /// The number of seconds clients must wait before executing this decorated route again.
        /// </summary>
        public int Seconds { get; set; }
        /// <summary>
        /// A text message that will be sent to the client upon throttling.  You can include the token {n} to
        /// show this.Seconds in the message, e.g. "Wait {n} seconds before trying again".
        /// </summary>
        public string Message { get; set; } = "You may only perform this action every {n} seconds.";
        public override void OnActionExecuting(ActionExecutingContext c)
        {
            if(_throttleCache == null)
            {
                var cache = c.HttpContext.RequestServices.GetService(typeof(IThrottleCache));
                _throttleCache = (IThrottleCache)cache;
            }
            
            var key = string.Concat(Name, "-", c.HttpContext.Request.HttpContext.Connection.RemoteIpAddress);
            var allowExecute = _throttleCache.AddToCache(key, Seconds);
            if (!allowExecute)
            {
                if (String.IsNullOrEmpty(Message))
                    Message = "You may only perform this action every {n} seconds.";
                c.Result = new ContentResult { Content = Message.Replace("{n}", Seconds.ToString()) };
                // see 409 - http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
                c.HttpContext.Response.StatusCode = (int)HttpStatusCode.Conflict;
            }
        }
    }
}
Startup.cs or Program.cs - Register the services with DI
This example uses Startup.cs/ConfigureServices - Put the code somewhere after AddControllersWithViews).
For a project created in .NET6+ I think you'd add the equivalent between builder.Services.AddRazorPages(); and var app = builder.Build(); in program.cs. services would be builder.Services.
If you don't get the placement of this code right, the cache will be empty every time you check it.
// The cache for throttling must be a singleton and requires IMemoryCache to be set up.
// Place it after AddControllersWithViews or AddRazorPages as they build a cache themselves
// Need this for IThrottleCache to work.
services.AddMemoryCache(_ => new MemoryCacheOptions
{
    SizeLimit = 1024, /* TODO: CHECK THIS IS THIS THE RIGHT SIZE FOR YOU! */
    CompactionPercentage = .3,
    ExpirationScanFrequency = TimeSpan.FromSeconds(30),
});
services.AddSingleton<IThrottleCache, ThrottleCache>();
Example Usage
[HttpGet, Route("GetTest")]
[ThrottleByIPAddress(Name = "MyControllerGetTest", Seconds = 5)]
public async Task<ActionResult<string>> GetTest()
{
    return "Hello world";
}
To help understand caching in .NET 5+, I've also made a caching console demo.