在 IMemoryCache 上调用扩展方法 GetOrCreateAsync 时是否可以避免缓存?

Is it possible to avoid caching when calling the extension method GetOrCreateAsync on an IMemoryCache?

我正在使用 IMemoryCache 来缓存从身份服务器检索到的令牌。

过去我使用过 GetOrCreateAsync extension method available in the Microsoft.Extensions.Caching.Abstractions 库。 这非常有用,因为我可以同时定义功能和到期日期。

但是使用令牌,在请求完成之前,我不会知道 expires in x 秒数值。 我想通过根本不缓存令牌来解释不存在的值的用例。

我试过以下方法

var token = await this.memoryCache.GetOrCreateAsync<string>("SomeKey", async cacheEntry =>
{
    var jwt = await GetTokenFromServer();
    var tokenHasValidExpireValue = int.TryParse(jwt.ExpiresIn, out int tokenExpirationSeconds);
    
    if (tokenHasValidExpireValue)
    {
        cacheEntry.AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(tokenExpirationSeconds);
    }
    else // Do not cache value.  Just return it.
    {
        cacheEntry.AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(0); //Exception thrown.  Value needs to be positive.
    }

    return jwt.token;
}

如您所见,当我尝试设置无时间过期时抛出异常TimeSpan.FromSeconds(0)

除了分别调用 GetSet 方法之外,还有其他解决方法吗? 如果可能,我想使用 GetOrCreateAsync 方法。

您实际上无法使用当前的 extension 完成此操作,因为它总是会在 BEFORE 调用工厂方法之前创建一个条目。也就是说,您可以采用与 GetOrCreateAsync.

非常相似的方式将行为封装在您自己的扩展中
public static class CustomMemoryCacheExtensions
{
    public static async Task<TItem> GetOrCreateIfValidTimestampAsync<TItem>(
        this IMemoryCache cache, object key, Func<Task<(int, TItem)>> factory)
    {
        if (!cache.TryGetValue(key, out object result))
        {
            (int tokenExpirationSeconds, TItem factoryResult) = 
                await factory().ConfigureAwait(false);

            if (tokenExpirationSeconds <= 0)
            {
                // if the factory method did not return a positive timestamp,
                // return the data without caching.
                return factoryResult;
            }

            // since we have a valid timestamp:
            // 1. create a cache entry
            // 2. Set the result
            // 3. Set the timestamp
            using ICacheEntry entry = cache.CreateEntry(key);

            entry.Value = result;
            entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(tokenExpirationSeconds);
        }

        return (TItem)result;
    }
}

然后您可以以非常相似的方式调用您的扩展方法:

var memoryCache = new MemoryCache(new MemoryCacheOptions());
var token = await memoryCache.GetOrCreateIfValidTimestampAsync<string>("SomeKey", async () =>
{
    var jwt = await GetTokenFromServer();
    var tokenHasValidExpireValue = int.TryParse(jwt.ExpiresIn, out int tokenExpirationSeconds);

    return (tokenExpirationSeconds, jwt.token);
}

我有这个需求,只是将过期时间设置为现在,我想你也可以将它设置为过去的时间,以确保:

// Don't wanna cache if this is the result
if (key == null || key.Expiration < DateTime.UtcNow)
{
    entry.AbsoluteExpiration = DateTimeOffset.Now;
    return null;
}