Skip to content

Commit

Permalink
update MemoryCache hack extension to support the new implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
13xforever committed Oct 15, 2024
1 parent bc07b77 commit 2d47dcc
Show file tree
Hide file tree
Showing 2 changed files with 29 additions and 8 deletions.
28 changes: 21 additions & 7 deletions CompatBot/Utils/Extensions/MemoryCacheExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,29 +8,43 @@ namespace CompatBot.Utils;

internal static class MemoryCacheExtensions
{
private static readonly object throaway = new object();

public static List<T> GetCacheKeys<T>(this MemoryCache memoryCache)
{
// idk why it requires access before it populates the internal state
memoryCache.TryGetValue("str", out _);
memoryCache.TryGetValue(throaway, out _);

// get the internal state object
var stateField = memoryCache.GetType()
.GetFields(BindingFlags.NonPublic | BindingFlags.Instance)
.FirstOrDefault(fi => fi.Name == "_coherentState");
var coherentState = stateField?.GetValue(memoryCache);
if (coherentState is null)
{
Config.Log.Error($"Looks like {nameof(MemoryCache)} internals have changed");
return new();
return [];
}

var field = coherentState.GetType()
// get the actual underlying key-value object
var stringField = coherentState.GetType()
.GetFields(BindingFlags.NonPublic | BindingFlags.Instance)
.FirstOrDefault(fi => fi.Name == "_entries");
if (field is null)
.FirstOrDefault(fi => fi.Name == "_stringEntries");
var nonStringField = coherentState.GetType()
.GetFields(BindingFlags.NonPublic | BindingFlags.Instance)
.FirstOrDefault(fi => fi.Name == "_nonStringEntries");
if (stringField is null || nonStringField is null)
{
Config.Log.Error($"Looks like {nameof(MemoryCache)} internals have changed");
return new();
return [];
}

var value = (IDictionary?)field.GetValue(coherentState);
return value?.Keys.OfType<T>().ToList() ?? new List<T>();
// read the keys
var value = typeof(T) == typeof(string)
? (IDictionary?)stringField.GetValue(coherentState)
: (IDictionary?)nonStringField.GetValue(coherentState);
return value?.Keys.OfType<T>().ToList() ?? [];
}

public static Dictionary<TKey, ICacheEntry?> GetCacheEntries<TKey>(this MemoryCache memoryCache)
Expand Down
9 changes: 8 additions & 1 deletion Tests/MemoryCacheExtensionTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,14 @@ public void GetCacheKeysTest()
var cache = new MemoryCache(new MemoryCacheOptions { ExpirationScanFrequency = TimeSpan.FromHours(1) });
Assert.That(cache.GetCacheKeys<int>(), Is.Empty);

cache.Set(13, "val13");
const string testVal = "vale13";
cache.Set(13, testVal);
Assert.Multiple(() =>
{
Assert.That(cache.TryGetValue(13, out string expectedVal), Is.True);
Assert.That(expectedVal, Is.EqualTo(testVal));
}
);
Assert.That(cache.GetCacheKeys<int>(), Has.Count.EqualTo(1));
}

Expand Down

0 comments on commit 2d47dcc

Please sign in to comment.