【发布时间】:2018-01-17 17:16:39
【问题描述】:
简明扼要。是否可以在 .Net Core Web 应用程序中列出内存缓存中的所有注册键?
我在 IMemoryCache 接口中没有找到任何东西。
【问题讨论】:
-
我的answer here 可能对你有用:
-
是的。感谢分享曾。
标签: c# asp.net-mvc caching asp.net-core
简明扼要。是否可以在 .Net Core Web 应用程序中列出内存缓存中的所有注册键?
我在 IMemoryCache 接口中没有找到任何东西。
【问题讨论】:
标签: c# asp.net-mvc caching asp.net-core
.Net Core 中还没有这样的东西。 这是我的解决方法:
var field = typeof(MemoryCache).GetProperty("EntriesCollection", BindingFlags.NonPublic | BindingFlags.Instance);
var collection = field.GetValue(_memoryCache) as ICollection;
var items = new List<string>();
if (collection != null)
foreach (var item in collection)
{
var methodInfo = item.GetType().GetProperty("Key");
var val = methodInfo.GetValue(item);
items.Add(val.ToString());
}
【讨论】:
MarkM 的回答对我来说不太奏效,它不会将结果转换为 ICollection,但我接受了这个想法并提出了这个对我来说非常有效的方法。希望它也可以帮助其他人:
// Get the empty definition for the EntriesCollection
var cacheEntriesCollectionDefinition = typeof(MemoryCache).GetProperty("EntriesCollection", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
// Populate the definition with your IMemoryCache instance.
// It needs to be cast as a dynamic, otherwise you can't
// loop through it due to it being a collection of objects.
var cacheEntriesCollection = cacheEntriesCollectionDefinition.GetValue(instanceIMemoryCache) as dynamic;
// Define a new list we'll be adding the cache entries too
List<Microsoft.Extensions.Caching.Memory.ICacheEntry> cacheCollectionValues = new List<Microsoft.Extensions.Caching.Memory.ICacheEntry>();
foreach (var cacheItem in cacheEntriesCollection)
{
// Get the "Value" from the key/value pair which contains the cache entry
Microsoft.Extensions.Caching.Memory.ICacheEntry cacheItemValue = cacheItem.GetType().GetProperty("Value").GetValue(cacheItem, null);
// Add the cache entry to the list
cacheCollectionValues.Add(cacheItemValue);
}
// You can now loop through the cacheCollectionValues list created above however you like.
【讨论】:
正如其他答案所指出的那样,Microsoft.Extensions.Caching.Memory.MemoryCache 的当前实现不公开任何允许检索所有缓存键的成员,尽管如果我们使用反射可以解决这个问题。
此答案基于the one by MarkM,通过将反射使用量降至最低并将所有内容打包到单个扩展类中来提高解决方案的速度。
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Microsoft.Extensions.Caching.Memory;
public static class MemoryCacheExtensions
{
private static readonly Func<MemoryCache, object> GetEntriesCollection = Delegate.CreateDelegate(
typeof(Func<MemoryCache, object>),
typeof(MemoryCache).GetProperty("EntriesCollection", BindingFlags.NonPublic | BindingFlags.Instance).GetGetMethod(true),
throwOnBindFailure: true) as Func<MemoryCache, object>;
public static IEnumerable GetKeys(this IMemoryCache memoryCache) =>
((IDictionary)GetEntriesCollection((MemoryCache)memoryCache)).Keys;
public static IEnumerable<T> GetKeys<T>(this IMemoryCache memoryCache) =>
GetKeys(memoryCache).OfType<T>();
}
用法:
var cache = new MemoryCache(new MemoryCacheOptions());
cache.GetOrCreate(1, ce => "one");
cache.GetOrCreate("two", ce => "two");
foreach (var key in cache.GetKeys())
Console.WriteLine($"Key: '{key}', Key type: '{key.GetType()}'");
foreach (var key in cache.GetKeys<string>())
Console.WriteLine($"Key: '{key}', Key type: '{key.GetType()}'");
输出:
Key: '1', Key type: 'System.Int32'
Key: 'two', Key type: 'System.String'
Key: 'two', Key type: 'System.String'
注意事项:
GetEntriesCollection delegate 的单个调用。当我们使用EntriesCollection 的键时,不使用反射。当我们遍历大量 MemoryCache 的键时,这可以节省一些时间和资源。MemoryCache.EntriesCollection 属性转换为IDictionary,尽管它的支持字段MemoryCache._entries 是ConcurrentDictionary<object, CacheEntry> 类型。我们无法将其直接转换为该类型,因为 CacheEntry 类型是内部的。【讨论】:
目前IMemoryCache接口中没有返回所有缓存键的方法。根据this github issue cmets,我认为将来不会添加。
引用Eilons评论
我认为这是否可用是值得怀疑的,因为部分想法 缓存就是在你提出问题后的片刻, 答案可能已经改变。也就是说,假设你有答案 有哪些键 - 稍后缓存被清除并且列表 您拥有的密钥无效。
如果您需要密钥,则应在将项目设置到缓存并根据需要使用时维护应用中的密钥列表。
这是另一个有用的 github 问题
【讨论】:
我们实现了这个概念以启用通过正则表达式模式删除。
完整的实现可以在Saturn72 github repository 中找到。
这些天我们正在转移到 AspNet Core,因此该位置可能会移动。
在存储库中搜索MemoryCacheManager
This is the current location
【讨论】:
我使用的是 MemoryCacheExtensions,建议 in roxton’s answer 显示键列表 lazyCache,将 MemoryCache 包装为 MemoryCacheProvider
public static IEnumerable<string> GetKeys(this IAppCache appCache)
{
var cacheProvider = appCache.CacheProvider as MemoryCacheProvider;
if (cacheProvider != null) //may be MockCacheProvider in tests
{
var field = typeof(MemoryCacheProvider).GetField("cache", BindingFlags.NonPublic | BindingFlags.Instance);
var memoryCache = field.GetValue(cacheProvider) as MemoryCache;
return memoryCache.GetKeys<string>();
}
return new List<string>();
}
【讨论】:
我知道这不是答案,但是... 还有另一种方法,您可以缓存一个列表。像这样的:
public async Task<List<User>> GetUsers()
{
var cacheKey = "getAllUserCacheKey";
if (_usersCache != null && ((MemoryCache)_usersCache).Count > 0)
{
return _usersCache.Get<List<User>>(cacheKey);
}
var users = await _userRepository.GetAll();
// Set cache options.
var cacheEntryOptions = new MemoryCacheEntryOptions()
.SetSlidingExpiration(TimeSpan.FromMinutes(5));
_usersCache.Set(cacheKey, users);
return users;
}
【讨论】:
_cache.Add("a",1234, DateTimeOffset.Now.AddMinutes(60));
_cache.Add("b",5678, DateTimeOffset.Now.AddMinutes(60));
var keys = _cache.Where(o => true).Select(o => o.Key);
foreach (var key in keys)
{
Console.WriteLine(key);
}
【讨论】: