To be succinct. Is possible list all register keys from Memory Cache in the .Net Core Web Application?
I didn't find anything in IMemoryCache interface.
To be succinct. Is possible list all register keys from Memory Cache in the .Net Core Web Application?
I didn't find anything in IMemoryCache interface.
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());
}
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:
GetEntriesCollection delegate. When we're working with EntriesCollection's keys, reflection is not used. This can save some time and resources when we're traversing a long collection of MemoryCache's keys.MemoryCache.EntriesCollection property to IDictionary despite that its backing field MemoryCache._entries is of type ConcurrentDictionary<object, CacheEntry>. We cannot cast it directly to that type because CacheEntry 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.
I was using MemoryCacheExtensions, suggested in roxton’s answer to show keys list for lazyCache, that wraps MemoryCache as 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>();
}
I know it's not the answer, but... There is another approach to this, you could cache a list. Something like this:
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);
}