You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.NET 6中IMemoryCache缓存意外过期:特性还是Bug?

IMemoryCache:特性还是Bug?

我在.NET 6中对IMemoryCache做了基础测试,测试结果如下:

  • PersonAsync符合预期:1秒后过期,从FetchPersonAsync获取新值
  • PlaceAsync符合预期:5秒后过期,从FetchPlaceAsync获取新值
  • MixedAsync不符合预期:设置的是60秒过期,但实际1秒后就过期,从FetchMixedAsync获取新值

MixedAsync的返回值通过调用PersonAsync和PlaceAsync构建,看起来IMemoryCache因为它依赖了PersonAsync的缓存数据,将其过期时间判定为1秒。

这其实是IMemoryCache(Microsoft.Extensions.Caching.Memory)的设计特性,并非Bug。

原因在于:当你在创建某一缓存条目的回调(比如MIXED的GetOrCreateAsync回调)中访问其他缓存条目时,IMemoryCache会自动为当前条目添加对这些依赖条目的关联。一旦依赖条目过期或被移除,当前条目也会被标记为过期。你的MIXED条目同时依赖了PERSON(1秒过期)和PLACE(5秒过期),因此它会跟随最早过期的PERSON条目一同失效。

如果想避免这种自动依赖,可通过以下方式处理:

  1. 在FetchMixedAsync中直接调用FetchPersonAsync和FetchPlaceAsync,跳过缓存层
  2. 创建MIXED缓存条目时禁用自动依赖跟踪:在cacheEntry上设置cacheEntry.SetOptions(new MemoryCacheEntryOptions { TrackDependencies = false })后再配置过期时间
  3. 手动管理依赖关系,比如只关联指定条目,或使用CacheToken控制过期

测试代码如下:

public class Query
{
    private readonly IMemoryCache _memoryCache;

    public Query(IMemoryCache memoryCache)
    {
        _memoryCache = memoryCache;
    }

    public async Task<Person> PersonAsync(CancellationToken cancellationToken)
    {
        return await _memoryCache.GetOrCreateAsync
        (
            "PERSON",
            cacheEntry => {
                cacheEntry.AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(1);
                return FetchPersonAsync(cancellationToken);
            }
        ).ConfigureAwait(false);
    }

    public async Task<Place> PlaceAsync(CancellationToken cancellationToken)
    {
        return await _memoryCache.GetOrCreateAsync
        (
            "PLACE", 
            cacheEntry => {
                cacheEntry.AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(5);
                return FetchPlaceAsync(cancellationToken);
            }
        ).ConfigureAwait(false);
    }

    public async Task<Mixed> MixedAsync(CancellationToken cancellationToken)
    {
        return await _memoryCache.GetOrCreateAsync
        (
            "MIXED",
            cacheEntry => {
                cacheEntry.AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(60);
                return FetchMixedAsync(cancellationToken);
            }
        ).ConfigureAwait(false);
    }

  
    internal async Task<Person> FetchPersonAsync(CancellationToken cancellationToken)
    {
        await Task.CompletedTask.ConfigureAwait(false);
        return new Person
        {
            Name = Guid.NewGuid().ToString()
        };
    }

    internal async Task<Place> FetchPlaceAsync(CancellationToken cancellationToken)
    {
        await Task.CompletedTask.ConfigureAwait(false);
        return new Place
        {
            Name = Guid.NewGuid().ToString()
        };
    }

    internal async Task<Mixed> FetchMixedAsync(CancellationToken cancellationToken)
    {
        var person = await PersonAsync(cancellationToken).ConfigureAwait(false);
        var place = await PlaceAsync(cancellationToken).ConfigureAwait(false);

        return new Mixed
        {
           BaseLineName = person.Name,
           FacetName = place.Name,
        };
    }
}

内容的提问来源于stack exchange,提问作者Mark Rover2341

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.25 23:27:33