diff --git a/src/libraries/Microsoft.Extensions.Caching.Memory/src/CacheEntry.CacheEntryTokens.cs b/src/libraries/Microsoft.Extensions.Caching.Memory/src/CacheEntry.CacheEntryTokens.cs index 5800a40118809..94e7144bb9499 100644 --- a/src/libraries/Microsoft.Extensions.Caching.Memory/src/CacheEntry.CacheEntryTokens.cs +++ b/src/libraries/Microsoft.Extensions.Caching.Memory/src/CacheEntry.CacheEntryTokens.cs @@ -23,7 +23,7 @@ private sealed class CacheEntryTokens internal List ExpirationTokens => _expirationTokens ??= new List(); internal List PostEvictionCallbacks => _postEvictionCallbacks ??= new List(); - internal void AttachTokens() + internal void AttachTokens(CacheEntry cacheEntry) { if (_expirationTokens != null) { @@ -35,7 +35,7 @@ internal void AttachTokens() if (expirationToken.ActiveChangeCallbacks) { _expirationTokenRegistrations ??= new List(1); - IDisposable registration = expirationToken.RegisterChangeCallback(ExpirationCallback, this); + IDisposable registration = expirationToken.RegisterChangeCallback(ExpirationCallback, cacheEntry); _expirationTokenRegistrations.Add(registration); } } diff --git a/src/libraries/Microsoft.Extensions.Caching.Memory/src/CacheEntry.cs b/src/libraries/Microsoft.Extensions.Caching.Memory/src/CacheEntry.cs index 606f0adac088e..8ac62a6524e16 100644 --- a/src/libraries/Microsoft.Extensions.Caching.Memory/src/CacheEntry.cs +++ b/src/libraries/Microsoft.Extensions.Caching.Memory/src/CacheEntry.cs @@ -199,7 +199,7 @@ bool FullCheck(in DateTimeOffset offset) } } - internal void AttachTokens() => _tokens?.AttachTokens(); + internal void AttachTokens() => _tokens?.AttachTokens(this); private static void ExpirationTokensExpired(object obj) { diff --git a/src/libraries/Microsoft.Extensions.Caching.Memory/tests/TokenExpirationTests.cs b/src/libraries/Microsoft.Extensions.Caching.Memory/tests/TokenExpirationTests.cs index 3da2515de8789..182d53cf2e8b9 100644 --- a/src/libraries/Microsoft.Extensions.Caching.Memory/tests/TokenExpirationTests.cs +++ b/src/libraries/Microsoft.Extensions.Caching.Memory/tests/TokenExpirationTests.cs @@ -207,6 +207,35 @@ public void TokenExpiresOnRegister() Assert.Null(result); } + [Fact] + public void PostEvictionCallbacksGetInvokedWhenMemoryCacheEntriesExpireWithAnActiveChangeToken() + { + using var cache = new MemoryCache(new MemoryCacheOptions()); + var key = new object(); + + var cts = new CancellationTokenSource(); + var callbackInvoked = new ManualResetEvent(false); + + cache.Set(key, new object(), new MemoryCacheEntryOptions + { + ExpirationTokens = { new CancellationChangeToken(cts.Token) }, + PostEvictionCallbacks = + { + new PostEvictionCallbackRegistration() + { + EvictionCallback = (key, value, reason, state) => ((ManualResetEvent)state).Set(), + State = callbackInvoked + } + } + }); + + Assert.True(cache.TryGetValue(key, out _)); + + cts.Cancel(); + Assert.True(callbackInvoked.WaitOne(TimeSpan.FromSeconds(10))); + Assert.False(cache.TryGetValue(key, out _)); + } + internal class TestToken : IChangeToken { private bool _hasChanged;