如何在 asp.net core 中遍历 MemoryCache?

Posted

技术标签:

【中文标题】如何在 asp.net core 中遍历 MemoryCache?【英文标题】:How to iterate through MemoryCache in asp.net core? 【发布时间】:2017-09-26 04:42:36 【问题描述】:

IMemoryCache 中没有可用的方法允许遍历每个缓存项。我的项目很小,我不想使用 Redis 之类的其他选项。

namepsace    Microsoft.Extensions.Caching.Memory
        public static class CacheExtensions
    
        public static object Get(this IMemoryCache cache, object key);
        public static TItem Get<TItem>(this IMemoryCache cache, object key);
        public static TItem GetOrCreate<TItem>(this IMemoryCache cache, object key, Func<ICacheEntry, TItem> factory);
        [AsyncStateMachine(typeof(CacheExtensions.<GetOrCreateAsync>d__9<>))]
        public static Task<TItem> GetOrCreateAsync<TItem>(this IMemoryCache cache, object key, Func<ICacheEntry, Task<TItem>> factory);
        public static TItem Set<TItem>(this IMemoryCache cache, object key, TItem value);
        public static TItem Set<TItem>(this IMemoryCache cache, object key, TItem value, DateTimeOffset absoluteExpiration);
        public static TItem Set<TItem>(this IMemoryCache cache, object key, TItem value, TimeSpan absoluteExpirationRelativeToNow);
        public static TItem Set<TItem>(this IMemoryCache cache, object key, TItem value, IChangeToken expirationToken);
        public static TItem Set<TItem>(this IMemoryCache cache, object key, TItem value, MemoryCacheEntryOptions options);
        public static bool TryGetValue<TItem>(this IMemoryCache cache, object key, out TItem value);
    

https://github.com/aspnet/Caching/blob/dev/src/Microsoft.Extensions.Caching.Memory/MemoryCache.cs

【问题讨论】:

我不认为有这样的功能,你可以做的简单的事情是将内存缓存包装在一个新的类中,该类存储缓存的键(预存储+每个项目集上的新条目)和一种迭代它们的方法,从 MemoryCache 调用 Get 方法。 迭代 MemoryCache 根本不是该类的用例。当然,您可以通过将缓存包装在另一个类中来添加它,但是您必须处理并发访问缓存导致的所有问题。 我想知道你为什么要遍历整个缓存中的所有项目? @Evk 说我有一些带有键模式的缓存项目:“abc.xyz-0”=> 用于单个项目,“abc.xyz”=> 用于所有项目。现在,如果我对项目数据进行更改,例如更改名称...,我想使用正则表达式或其他任何方式删除所有缓存的项目,以确保下一个请求将获得刷新更新的数据。 @Evk 好吧,MVC 5 中的内存缓存,你可以这样写。 System.Runtime.Caching.MemoryCache.Default.Select(p=> p.Key) 我知道 .net 核心一开始很简单。 【参考方案1】:

您应该缓存两种类型的项目。

    您按原样缓存您的属性,abc.xyz-0。 第二次缓存主键名下的属性列表,abc.xyz

示例代码:

cache.Set("abc.xyz-name", name, TimeSpan.FromMinutes(30));
cache.Set("abc.xyz-lastname", lastname, TimeSpan.FromMinutes(30));
cache.Set("abc.xyz-birthday", birthday, TimeSpan.FromMinutes(30));
cache.Set("abc.xyz", new List<string>  "abc.xyz-name", "abc.xyz-lastname", "abc.xyz-birthday" , TimeSpan.FromMinutes(30));

以及删除时:

var keys = cache.Get<List<string>>("abc.xyz");
foreach(var key in keys)
    cache.Remove(key);
cache.remove("abc.xyz");

大多数服务使用IDistributedCache(在您的情况下,注册时为MemoryDistributedCache - 它再次注入IMemoryCache,即MemoryCache 类)。

在分布式缓存中,您无法遍历所有键,因为可能存在数百万个键,如果您可以/将对其进行迭代,这将显着降低缓存服务的性能。

因此,上述解决方案也很友好,可以在您将内存缓存替换为分布式缓存(例如 Redis)的情况下做好准备。

【讨论】:

我想指出,除非实施了某种并发控制,否则这种解决方案不是并发证明的;考虑两个独立的执行流程在同一个存储桶下添加一个新属性——这可能会导致属性被添加到缓存中,但是存储桶错过了它; 我认为简单地用 Redis 缓存替换 MemoryCache 是一个梦想,我不明白他们为什么需要共享接口。对于一个你肯定不想用同步方法来做你的分布式缓存。其次,您需要考虑到您的分布式缓存存在连接问题。 顺便说一句,你的答案的其余部分没有冒犯 - 只是批评 .NET Core 团队没有在 MemoryCache 上公开 Keys 属性。这意味着每个人都必须推出自己的解决方法,包括处理复杂的并发问题。 好吧,将它添加到接口将强制每个实现实现迭代,甚至是不支持它的商店。再加上它对性能不利,您需要在迭代期间锁定键(因为它们可能会在迭代时过期)然后成为应该加速的组件的瓶颈【参考方案2】:

对于大型项目,这有点尴尬。我建议您使用包装类 MemoryCacheManager 并将其注册为 Singleton。 https://gist.github.com/vlapenkov/0a66c40221f9c56d12eb0420fb7cef77

使用

_manager.Get("key", ()=>"value") // to set a key,

_manager.GetKeys() // get all keys

_manager.Remove("key") //remove one key

_manager.Clear() //remove all keys

【讨论】:

嗨@Lapenkov,聪明的解决方案!为什么_allKeys 被标记为static?由于这属于 IMemoryCache - 这不应该具有相同的生命周期吗? @Lapenkov - 我认为这个解决方案将在单个服务器上运行,因为上面链接的解决方案中提到的“_allKeys”字典将在每台机器上维护 - 或者我可能缺少一些东西。 .

以上是关于如何在 asp.net core 中遍历 MemoryCache?的主要内容,如果未能解决你的问题,请参考以下文章

3AutoMapper In Asp.net Core

ASP.NET Core中的缓存[1]:如何在一个ASP.NET Core应用中使用缓存

如何在 ASP.Net Core 中使用 IHostedService

asp.net core1.x/asp.net core2.0中如何加载多个配置文件

如何使用 EF Core 在 ASP.NET Core 中取消应用迁移

如何在 ASP.NET Core 中使用 SqlClient?