当前位置: 欣欣网 > 码农

.NET 9 Preview 1 中 MemoryCache 的更新

2024-02-24码农

.NET 9 Preview 1 中 MemoryCache 的更新

Intro

.NET 9 Preview 1 中为 MemoryCache 添加了一个 Keys 属性,我们可以通过它来获取 memory cache 中的缓存的 key list 了,另外新增了一个带 MemoryCacheEntryOptions 参数的 GetOrCreate(Async) 重载方法

Sample

Keys Sample

新增的 API:

public classMemoryCache
{
public System.Collections.Generic.IEnumerable<object> Keys { get; }
}

来看下使用示例:

Console.WriteLine("MemoryCache keys non-di sample");
var memoryCache = new MemoryCache(new MemoryCacheOptions());
memoryCache.Set("user:1:roles""user,manager");
memoryCache.Set("user:1:permission""read,write");
memoryCache.Set("user:2:roles""user");
memoryCache.Set("user:2:permission""read");
foreach (var cacheKey in memoryCache.Keys)
{
Console.WriteLine(cacheKey.ToString());
if (cacheKey isstring cacheKeyStr && cacheKeyStr.StartsWith("user:2:"))
{
memoryCache.Remove(cacheKey);
}
}
Console.WriteLine("some keys had been removed");
foreach (var cacheKey in ((MemoryCache)memoryCache).Keys)
{
Console.WriteLine(cacheKey.ToString());
}

这个示例是直接 new 不使用依赖注入的方式,这里模拟了清除用户缓存的一个操作,将用户 2 的缓存删除并打印所有的 cache key

输出结果如下:

keys-non-di-sample-output

再来看一下使用依赖注入的示例,大体上差不多,只是我们需要转换一下类型,因为这个 Keys 是定义在 MemoryCache 类型上, IMemoryCache 接口并未定义

Console.WriteLine("MemoryCache keys dependency injection sample");
usingvar services = new ServiceCollection()
.AddMemoryCache()
.BuildServiceProvider();
var memoryCache = services.GetRequiredService<IMemoryCache>();
memoryCache.Set("user:1:roles""user,manager");
memoryCache.Set("user:1:permission""read,write");
memoryCache.Set("user:2:roles""user");
memoryCache.Set("user:2:permission""read");
foreach (var cacheKey in ((MemoryCache)memoryCache).Keys)
{
Console.WriteLine(cacheKey.ToString());
if (cacheKey isstring cacheKeyStr && cacheKeyStr.StartsWith("user:2:"))
{
memoryCache.Remove(cacheKey);
}
}
Console.WriteLine("some keys had been removed");
foreach (var cacheKey in ((MemoryCache)memoryCache).Keys)
{
Console.WriteLine(cacheKey.ToString());
}

输出结果如下:

keys-di-sample-output

GetOrCreate(Async) override

新的 API 定义如下:

 namespace Microsoft.Extensions.Caching.Memory {
public static class CacheExtensions {
+ public static TItem? GetOrCreate<TItem>(this IMemoryCache cache, object key, Func<ICacheEntry, TItem> factory, MemoryCacheEntryOptions? createOptions);
+ public static Task<TItem?> GetOrCreateAsync<TItem>(this IMemoryCache cache, object key, Func<ICacheEntry, Task<TItem>> factory, MemoryCacheEntryOptions? createOptions);
}
 }

在原来的 GetOrCreate / GetOrCreateAsync 方法的基础上增加了 MemoryCacheEntryOptions 参数,这样我们可以在 options 参数设置缓存的过期时间等,我们 factory 方法就能比较简单了

使用示例如下:

Console.WriteLine("MemoryCache keys dependency injection sample");
usingvar services = new ServiceCollection()
.AddMemoryCache()
.BuildServiceProvider();
var memoryCache = services.GetRequiredService<IMemoryCache>();
var key = "user:2:roles";
FetchValue();
Thread.Sleep(1000);
FetchValue();
Thread.Sleep(3000);
FetchValue();
voidFetchValue()
{
memoryCache.GetOrCreate("no-expiration", _ =>
{
Console.WriteLine("fetch value from source for no-expiration key");
return"user";
});
memoryCache.GetOrCreate(key, _ =>
{
Console.WriteLine("fetch value from source");
return"user";
},
new MemoryCacheEntryOptions() { AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(3) });
}

这里有两个缓存,用于对比设置和不设置的区别, no-expiration 没有设置过期时间,另外一个设置了三秒的过期时间,输出结果如下:

可以看到 fetch value from source 出现了两次,第一次调用的时候缓存不存所以从 source 去取走了 factory 的逻辑,最后一次缓存过期了所以再次从 source 拿了一次,而 no-expiration key 因为没有过期时间,所以只有在第一次取得时候从 source 取,后面都是从缓存获取

这个 API 的实现比较简单,蹭了一个 PR,哈哈

References

  • https://github.com/dotnet/runtime/issues/36554

  • https://github.com/dotnet/runtime/pull/94992/files

  • https://github.com/dotnet/runtime/issues/92101

  • https://github.com/dotnet/runtime/pull/94335