polly v8移除了memorycacheprovider,需自行实现icacheprovider或改用policy.wrap组合手动缓存。推荐用imemorycache+retrypolicy,避免cachepolicy复杂性。

为什么直接用 Polly.Caching.MemoryCacheProvider 会报错?
因为从 Polly v8 开始,MemoryCacheProvider 已被移除,官方不再内置对 IMemoryCache 的封装。你如果照着旧文档写 new MemoryCacheProvider(new MemoryCache(new MemoryCacheOptions())),编译直接失败——类型不存在。
根本原因是 Polly v8 抽离了缓存抽象,只保留 ICacheProvider<tkey tvalue></tkey> 接口,要求你自行实现或选用社区适配器。
- 别再找
Microsoft.Extensions.Http.Polly里的缓存类,它不包含缓存逻辑 -
IMemoryCache本身线程安全,但 Polly 的缓存策略(如CachePolicy)需要你把 key 转换、序列化、过期策略对齐都手动处理 - 最简路径是:用
Polly.Registry.PolicyRegistry管理策略 + 自己桥接IMemoryCache
如何用 CachePolicy 包裹 IMemoryCache 手动缓存?
核心思路不是让 Polly “接管” 缓存,而是用 Polly 的 CachePolicy 做决策,实际读写仍走 IMemoryCache。你需要自己定义 key 生成规则和 value 封装格式。
示例关键片段:
var cache = new MemoryCache(new MemoryCacheOptions());
var cachePolicy = Policy.Cache<string>(
new MemoryCacheProvider(cache),
ttlSeconds: 60,
onCacheGet: (key, ctx) => {
var cached = cache.Get<CacheEntry>(key);
return cached?.Value;
},
onCachePut: (key, value, ctx, span) => {
cache.Set(key, new CacheEntry { Value = value }, TimeSpan.FromSeconds(span.TotalSeconds));
}
);
但注意:上面代码中的 MemoryCacheProvider 是假想的——v8 没这个类。所以必须重写为:
- 自己实现
ICacheProvider<string object></string>,内部调用IMemoryCache - key 建议用
ctx.OperationKey ?? Guid.NewGuid().ToString(),避免策略复用时冲突 - value 必须可序列化(
IMemoryCache不限制类型,但 Polly 缓存策略默认 expectobject或泛型 T) - 不要在
onCachePut里传TimeSpan.Zero,否则缓存立即失效
推荐做法:绕过 CachePolicy,用 Policy.Wrap 组合 RetryPolicy + 手动缓存逻辑
多数真实场景并不需要 Polly 的缓存语义(比如自动 key 管理、多级缓存钩子),只需要“先查缓存,没命中再执行带重试的业务逻辑”。这时硬套 CachePolicy 反而增加复杂度。
更可控的做法是:
- 把缓存读写完全收口到一个方法里,例如
TryGetFromCacheAsync<t>(string key, Func<cancellationtoken task>> fallback)</cancellationtoken></t> - 用
Policy.WrapAsync包一层RetryPolicy和CircuitBreakerPolicy,只作用于fallback函数 -
IMemoryCache的 key 命名建议含参数哈希(如nameof(GetUser) + userId.GetHashCode()),避免拼接字符串出错 - 注意
IMemoryCache.GetOrCreateAsync的 lambda 是 lazy 执行的,里面调用的fallback应该已包裹好 Polly 策略
这样既利用了 IMemoryCache 的成熟 API,又没丢掉 Polly 的容错能力,还避开了 v8 缓存抽象的坑。
容易被忽略的生命周期和线程安全细节
IMemoryCache 是 singleton,但 CachePolicy 实例不是线程安全的——不能跨请求共享同一个 CachePolicy 实例,尤其当它内部持有非线程安全的缓存 provider 时。
- ASP.NET Core 中,始终通过 DI 获取
IMemoryCache,别 newMemoryCache实例 - 如果你在
onCacheGet里反序列化 JSON 字符串,记得 catchJsonException并返回 null,否则整个缓存策略会抛异常中断流程 -
CachePolicy的ttlSeconds和IMemoryCache的AbsoluteExpirationRelativeToNow不自动同步,必须手动对齐,否则出现“Polly 认为没过期,但 MemoryCache 已剔除”的情况 - 调试时留意
context.EarlyValue—— 它表示缓存命中时 Polly 返回的值,不是IMemoryCache.Get的原始结果,类型可能被包装过
真正麻烦的从来不是怎么写第一行代码,而是缓存 key 设计是否覆盖所有参数组合、过期时间单位有没有混用秒和毫秒、以及 fallback 函数里异常是否被 Polly 正确捕获而非穿透到上层。










