如何在asp.net核心中检索内存缓存键列表?

Car*_*los 6 c# asp.net-mvc caching asp.net-core

要简洁.是否可以在.Net Core Web应用程序中列出内存缓存中的所有注册密钥?

我在IMemoryCache界面中找不到任何东西.

小智 17

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.
Run Code Online (Sandbox Code Playgroud)

  • 该解决方案是迄今为止最好的解决方案 (6/2020) (3认同)

小智 16

在.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());
 }
Run Code Online (Sandbox Code Playgroud)

  • 这在 NET7 中不起作用。你有解决办法吗? (6认同)

Car*_*las 14

从 开始.NET 7,他们已经更改了对象的内部结构,并且有一个新的CoherentState私有类,它是实例内的私有字段MemoryCache,并且在 CoherentState 字段 ( _coherentState) 中,您可以访问你们一直在引用的 EntriesCollection 集合。因此,为了获取列表,keys您可以执行以下操作:

var coherentState = typeof(MemoryCache).GetField("_coherentState", BindingFlags.NonPublic | BindingFlags.Instance);

        var coherentStateValue = coherentState.GetValue(_memoryCache);

        var entriesCollection = coherentStateValue.GetType().GetProperty("EntriesCollection", BindingFlags.NonPublic | BindingFlags.Instance);

        var entriesCollectionValue = entriesCollection.GetValue(coherentStateValue) as ICollection;

        var keys = new List<string>();

        if (entriesCollectionValue != null)
        {
            foreach (var item in entriesCollectionValue)
            {
                var methodInfo = item.GetType().GetProperty("Key");

                var val = methodInfo.GetValue(item);

                keys.Add(val.ToString());
            }
        }
Run Code Online (Sandbox Code Playgroud)

我在本地测试过,有效!


rox*_*ton 8

正如其他答案所指出的那样,Microsoft.Extensions.Caching.Memory.MemoryCache 的当前实现没有公开任何允许检索所有缓存键的成员,尽管如果我们使用反射有办法解决这个问题。

这个答案建立在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>();
}
Run Code Online (Sandbox Code Playgroud)

用法:

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()}'");
Run Code Online (Sandbox Code Playgroud)

输出:

Key: '1', Key type: 'System.Int32'
Key: 'two', Key type: 'System.String'
Key: 'two', Key type: 'System.String'
Run Code Online (Sandbox Code Playgroud)

笔记:

  • 反射使用减少到构建GetEntriesCollection委托的单个调用。当我们使用EntriesCollection的键时,不使用反射。当我们遍历很长的MemoryCache's 键集合时,这可以节省一些时间和资源。
  • 在解决方案中,我们将MemoryCache.EntriesCollectionproperty强制转换为IDictionary尽管其支持字段MemoryCache._entries的类型为ConcurrentDictionary<object, CacheEntry>。我们不能将它直接转换为该类型,因为CacheEntry类型是内部的。

  • @Asons,答案现在支持 Microsoft.Extensions.Caching.Memory 7。请参阅注释中的验证详细信息。 (2认同)

Shy*_*yju 6

目前IMemoryCache接口中还没有返回所有缓存键的方法。根据这个 github 问题评论,我认为将来不会添加。

引用Eilons 的评论

我认为这是否可用是值得怀疑的,因为缓存的部分想法是在您提出问题后不久,答案可能会改变。也就是说,假设您知道存在哪些键 - 片刻之后缓存被清除并且您拥有的键列表无效。

如果您需要密钥,您应该在将项目设置到缓存并根据需要使用它时维护应用程序中的密钥列表。

这是另一个有用的 github 问题

MemoryCache 会有 GetEnumerator() 吗?