How to retrieve a list of Memory Cache keys in asp.net core?
Currently there is no such method in the IMemoryCache
interface to return all the cache keys. As per this github issue comments, i do not think that would be added in the future.
Quoting Eilons comment
I think it's doubtful this would be available because part of the idea with caching is that mere moments after you ask it a question, the answer could have changed. That is, suppose you have the answer to which keys are there - a moment later the cache is purged and the list of keys you have is invalid.
If you need the keys, you should maintain the list of keys in your app while you set items to the cache and use that as needed.
Here is another useful github issue
Will there be GetEnumerator() for MemoryCache ?
As other answers point out, current implementation of Microsoft.Extensions.Caching.Memory.MemoryCache does not expose any members allowing to retrieve all cache keys, although there is a way around the problem if we use reflection.
This answer builds upon the one by MarkM, adds some speed to the solution by reducing reflection usage to a minimum and packs everything into a single extension class.
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>();
}
Usage:
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()}'");
Output:
Key: '1', Key type: 'System.Int32'
Key: 'two', Key type: 'System.String'
Key: 'two', Key type: 'System.String'
Notes:
- Reflection usage is reduced to a single call that builds the
GetEntriesCollection
delegate. When we're working withEntriesCollection
's keys, reflection is not used. This can save some time and resources when we're traversing a long collection ofMemoryCache
's keys. - In the solution we are casting
MemoryCache.EntriesCollection
property toIDictionary
despite that its backing fieldMemoryCache._entries
is of typeConcurrentDictionary<object, CacheEntry>
. We cannot cast it directly to that type becauseCacheEntry
type is internal.
MarkM's answer didn't quite work for me, it wouldn't cast the results to an ICollection, but I took the idea and came up with this that works quite well for me. Hopefully it helps someone else out there too:
// 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.
There is no such thing in .Net Core yet. Here is my workaround:
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());
}