【问题标题】:Is it possible to avoid caching when calling the extension method GetOrCreateAsync on an IMemoryCache?在 IMemoryCache 上调用扩展方法 GetOrCreateAsync 时是否可以避免缓存?
【发布时间】:2021-01-14 19:27:03
【问题描述】:

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

过去我使用过Microsoft.Extensions.Caching.Abstractions 库中提供的GetOrCreateAsync 扩展方法。 这非常有用,因为我可以同时定义功能和到期日期。

但是,使用令牌,在请求完成之前,我不会知道 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 方法。

【问题讨论】:

  • 使用GetOrCreateAsync 除了令人困惑的行为之外,您还有什么收获?如果您没有始终如一地创建条目,则调用 GetOrCreateAsync 零意义。
  • @DavidL 它简化了我的代码
  • 但是显然不是。正如您所展示的,您对数据有不同的期望,这意味着该方法不适合您的用例。

标签: c# .net .net-core memorycache


【解决方案1】:

您实际上无法使用当前的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);
}

【讨论】:

    【解决方案2】:

    我有这个需要,只需将过期时间设置为现在,我假设您也可以将其设置为过去的时间以确保:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-04-07
      • 2023-04-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-03-12
      • 2022-06-10
      • 1970-01-01
      相关资源
      最近更新 更多