【问题标题】:How to retrieve a list of Memory Cache keys in asp.net core?如何在 asp.net 核心中检索内存缓存键列表?
【发布时间】:2018-01-17 17:16:39
【问题描述】:

简明扼要。是否可以在 .Net Core Web 应用程序中列出内存缓存中的所有注册键?

我在 IMemoryCache 接口中没有找到任何东西。

【问题讨论】:

  • 我的answer here 可能对你有用:
  • 是的。感谢分享曾。

标签: c# asp.net-mvc caching asp.net-core


【解决方案1】:

.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());
 }

【讨论】:

  • 看起来实体框架也使用了MemoryCache,所以如果你只想要你的应用程序的键,你可能想过滤掉所有以“Microsoft”开头的键。
  • 太棒了,谢谢。一定会喜欢 System.Reflection,但很奇怪的是,微软没有包含用于处理密钥等的额外扩展方法。
【解决方案2】:

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.

【讨论】:

  • 这个解决方案是迄今为止最好的(2020 年 6 月)
【解决方案3】:

正如其他答案所指出的那样,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'

注意事项:

  • 反射使用减少为builds the GetEntriesCollection delegate 的单个调用。当我们使用EntriesCollection 的键时,不使用反射。当我们遍历大量 MemoryCache 的键时,这可以节省一些时间和资源。
  • 在解决方案中,我们将MemoryCache.EntriesCollection 属性转换为IDictionary,尽管它的支持字段MemoryCache._entriesConcurrentDictionary&lt;object, CacheEntry&gt; 类型。我们无法将其直接转换为该类型,因为 CacheEntry 类型是内部的。

【讨论】:

    【解决方案4】:

    目前IMemoryCache接口中没有返回所有缓存键的方法。根据this github issue cmets,我认为将来不会添加。

    引用Eilons评论

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

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

    这是另一个有用的 github 问题

    Will there be GetEnumerator() for MemoryCache ?

    【讨论】:

    • 假设我需要在特定业务情况下排除缓存寄存器,应用程序中的键列表可能会很有用。谢谢Shyju
    • @Carlos 您应该保持一个数据结构,当您的代码从 IMemoryCache 写入/删除条目时,您需要保持同步。保持同步并不是一个容易解决恕我直言的问题。
    【解决方案5】:

    我们实现了这个概念以启用通过正则表达式模式删除。

    完整的实现可以在Saturn72 github repository 中找到。 这些天我们正在转移到 AspNet Core,因此该位置可能会移动。 在存储库中搜索MemoryCacheManager This is the current location

    【讨论】:

      【解决方案6】:

      我使用的是 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>();
      }
      

      【讨论】:

        【解决方案7】:

        我知道这不是答案,但是... 还有另一种方法,您可以缓存一个列表。像这样的:

        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;
        
        }
        

        【讨论】:

          【解决方案8】:
              _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);
                  }
          

          【讨论】:

            猜你喜欢
            • 2020-12-05
            • 2017-11-05
            • 2023-03-11
            • 1970-01-01
            • 2012-08-02
            • 1970-01-01
            • 2021-04-11
            • 2020-07-16
            • 2021-10-18
            相关资源
            最近更新 更多