Skip to content

Commit

Permalink
breaking change in internals memory cache
Browse files Browse the repository at this point in the history
  • Loading branch information
qdraw committed Oct 10, 2024
1 parent 071e0f2 commit dca8a3d
Show file tree
Hide file tree
Showing 2 changed files with 81 additions and 78 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,60 +7,64 @@
using System.Reflection.Emit;
using Microsoft.Extensions.Caching.Memory;

namespace starsky.foundation.platform.Extensions
namespace starsky.foundation.platform.Extensions;

/// <summary>
/// @see: https://stackoverflow.com/a/64291008
/// </summary>
[SuppressMessage("Usage", "S3011:Make sure that this accessibility bypass is safe here",
Justification = "Safe")]
public static class MemoryCacheExtensions
{
private static readonly Lazy<Func<MemoryCache, object>>? GetCoherentState =
new(() =>
CreateGetter<MemoryCache, object>(typeof(MemoryCache)
.GetField("_coherentState", BindingFlags.NonPublic | BindingFlags.Instance)!));

/// <summary>
/// @see: https://stackoverflow.com/a/64291008
/// https://github.com/dotnet/runtime/blob/81cabf2857a01351e5ab578947c7403a5b128ad1/src/libraries/Microsoft.Extensions.Caching.Memory/src/MemoryCache.cs#L680C1-L681C85
/// </summary>
[SuppressMessage("Usage", "S3011:Make sure that this accessibility bypass is safe here", Justification = "Safe")]
public static class MemoryCacheExtensions
private static readonly Lazy<Func<object, IDictionary>> GetEntries7 =
new(() =>
CreateGetter<object, IDictionary>(typeof(MemoryCache)
.GetNestedType("CoherentState", BindingFlags.NonPublic)?
.GetField("_stringEntries", BindingFlags.NonPublic | BindingFlags.Instance)!));

private static readonly Func<MemoryCache, IDictionary> GetEntries =
cache => GetEntries7.Value(GetCoherentState.Value(cache));

private static Func<TParam, TReturn> CreateGetter<TParam, TReturn>(FieldInfo field)
{
private static readonly Lazy<Func<MemoryCache, object>>? GetCoherentState =
new(() =>
CreateGetter<MemoryCache, object>(typeof(MemoryCache)
.GetField("_coherentState", BindingFlags.NonPublic | BindingFlags.Instance)!));
var methodName = $"{field.ReflectedType?.FullName}.get_{field.Name}";
var method = new DynamicMethod(methodName, typeof(TReturn), new[] { typeof(TParam) },
typeof(TParam), true);
var ilGen = method.GetILGenerator();
ilGen.Emit(OpCodes.Ldarg_0);
ilGen.Emit(OpCodes.Ldfld, field);
ilGen.Emit(OpCodes.Ret);
return ( Func<TParam, TReturn> ) method.CreateDelegate(typeof(Func<TParam, TReturn>));
}

private static readonly Lazy<Func<object, IDictionary>> GetEntries7 =
new(() =>
CreateGetter<object, IDictionary>(typeof(MemoryCache)
.GetNestedType("CoherentState", BindingFlags.NonPublic)?
.GetField("_entries", BindingFlags.NonPublic | BindingFlags.Instance)!));
private static ICollection GetKeys(this IMemoryCache memoryCache)
{
return GetEntries(( MemoryCache ) memoryCache).Keys;
}

private static Func<TParam, TReturn> CreateGetter<TParam, TReturn>(FieldInfo field)
/// <summary>
/// Get Keys
/// </summary>
/// <param name="memoryCache">memory cache</param>
/// <typeparam name="T">bind as</typeparam>
/// <returns>list of items</returns>
public static IEnumerable<T> GetKeys<T>(this IMemoryCache memoryCache)
{
try
{
var methodName = $"{field.ReflectedType?.FullName}.get_{field.Name}";
var method = new DynamicMethod(methodName, typeof(TReturn), new[] { typeof(TParam) }, typeof(TParam), true);
var ilGen = method.GetILGenerator();
ilGen.Emit(OpCodes.Ldarg_0);
ilGen.Emit(OpCodes.Ldfld, field);
ilGen.Emit(OpCodes.Ret);
return ( Func<TParam, TReturn> ) method.CreateDelegate(typeof(Func<TParam, TReturn>));
return GetKeys(memoryCache).OfType<T>();
}

private static readonly Func<MemoryCache, IDictionary> GetEntries =
cache => GetEntries7.Value(GetCoherentState.Value(cache));

private static ICollection GetKeys(this IMemoryCache memoryCache) =>
GetEntries(( MemoryCache ) memoryCache).Keys;

/// <summary>
/// Get Keys
/// </summary>
/// <param name="memoryCache">memory cache</param>
/// <typeparam name="T">bind as</typeparam>
/// <returns>list of items</returns>
public static IEnumerable<T> GetKeys<T>(this IMemoryCache memoryCache)
catch ( InvalidCastException )
{
try
{
return GetKeys(memoryCache).OfType<T>();
}
catch ( InvalidCastException )
{
return new List<T>();
}
return new List<T>();
}


}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,43 +5,42 @@
using starsky.foundation.platform.Extensions;
using starskytest.FakeMocks;

namespace starskytest.starsky.foundation.platform.Extensions
namespace starskytest.starsky.foundation.platform.Extensions;

[TestClass]
public sealed class MemoryCacheExtensionsTest
{
[TestClass]
public sealed class MemoryCacheExtensionsTest
[TestMethod]
public void MemoryCacheExtensions_NoContentInCache()
{
[TestMethod]
public void NoContentInCache()
{
var provider = new ServiceCollection()
.AddMemoryCache();
var provider = new ServiceCollection()
.AddMemoryCache();

var buildServiceProvider = provider.BuildServiceProvider();
var memoryCache = buildServiceProvider.GetService<IMemoryCache>();
var keys = memoryCache?.GetKeys<string>();
Assert.AreEqual(0, keys?.Count());
}

var buildServiceProvider = provider.BuildServiceProvider();
var memoryCache = buildServiceProvider.GetService<IMemoryCache>();
var keys= memoryCache?.GetKeys<string>();
Assert.AreEqual(0, keys?.Count());
}

[TestMethod]
public void OneItemInCache()
{
var provider = new ServiceCollection()
.AddMemoryCache();
[TestMethod]
public void MemoryCacheExtensions_OneItemInCache()
{
var provider = new ServiceCollection()
.AddMemoryCache();

var buildServiceProvider = provider.BuildServiceProvider();
var memoryCache = buildServiceProvider.GetService<IMemoryCache>();
memoryCache?.Set("test", "");
var keys= memoryCache?.GetKeys<string>().ToList();
Assert.AreEqual(1, keys?.Count);
Assert.AreEqual("test", keys?[0]);
}
var buildServiceProvider = provider.BuildServiceProvider();
var memoryCache = buildServiceProvider.GetService<IMemoryCache>();
memoryCache?.Set("test", "");
var keys = memoryCache?.GetKeys<string>().ToList();
Assert.AreEqual(1, keys?.Count);
Assert.AreEqual("test", keys?[0]);
}

[TestMethod]
public void FakeCache_Invalid()
{
var cache = new FakeMemoryCache();
var keys= cache.GetKeys<string>().ToList();
Assert.AreEqual(0, keys.Count);
}
[TestMethod]
public void FakeCache_Invalid()
{
var cache = new FakeMemoryCache();
var keys = cache.GetKeys<string>().ToList();
Assert.AreEqual(0, keys.Count);
}
}

0 comments on commit dca8a3d

Please sign in to comment.