Skip to content

Commit a4ccff1

Browse files
Merge pull request #1 from CascadeJonathan/add-key-enumeration
Add dictionary to maintain cache keys
2 parents 33d055c + 03b7a17 commit a4ccff1

File tree

6 files changed

+155
-1
lines changed

6 files changed

+155
-1
lines changed

Console.Net461/Program.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,15 @@ static void Main()
2222
item = cache.GetOrAdd("Program.Main.Person", () => Tuple.Create("Joe Blogs", DateTime.UtcNow));
2323

2424
System.Console.WriteLine(item.Item1);
25+
26+
System.Console.WriteLine("Enumerating keys...");
27+
foreach (var key in cache.GetCacheKeys().Keys)
28+
{
29+
System.Console.WriteLine($"{key}");
30+
}
31+
System.Console.WriteLine("Finished enumerating keys...");
32+
33+
System.Console.ReadLine();
2534
}
2635
}
2736
}

LazyCache.UnitTests/CachingServiceMemoryCacheProviderTests.cs

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System;
22
using System.Collections.Generic;
33
using System.Diagnostics;
4+
using System.Linq;
45
using System.Threading;
56
using System.Threading.Tasks;
67
using FluentAssertions;
@@ -1125,5 +1126,93 @@ public void TryGetReturnsCachedValueAndTrue()
11251126

11261127
Assert.IsFalse(contains2);
11271128
}
1129+
1130+
[Test]
1131+
public void ListOfCacheKeysContainsAllKeysAfterCallingAdd()
1132+
{
1133+
List<string> keys = new List<string>(){"one", "two", "three", "four", "five"};
1134+
1135+
1136+
foreach (var key in keys)
1137+
{
1138+
sut.Add(key, key.Reverse());
1139+
}
1140+
1141+
var cachedKeys = sut.GetCacheKeys().Keys;
1142+
1143+
Assert.IsTrue(keys.Intersect(cachedKeys).Count() == keys.Count);
1144+
}
1145+
1146+
[Test]
1147+
public void ListOfCacheKeysContainsAllKeysAfterCallingGetOrAdd()
1148+
{
1149+
List<string> keys = new List<string>() { "one", "two", "three", "four", "five" };
1150+
1151+
foreach (var key in keys)
1152+
{
1153+
sut.GetOrAdd(key, () => key.Reverse());
1154+
}
1155+
1156+
var cachedKeys = sut.GetCacheKeys().Keys;
1157+
1158+
Assert.IsTrue(keys.Intersect(cachedKeys).Count() == keys.Count);
1159+
}
1160+
1161+
[Test]
1162+
public void ListOfCacheKeysContainsSomeKeysAfterCallingAddAndRemovingOne()
1163+
{
1164+
List<string> keys = new List<string>() { "one", "two", "three", "four", "five" };
1165+
1166+
foreach (var key in keys)
1167+
{
1168+
sut.Add(key, key.Reverse());
1169+
}
1170+
1171+
// remove three
1172+
sut.Remove("three");
1173+
1174+
var cachedKeys = sut.GetCacheKeys().Keys.ToList();
1175+
1176+
Assert.IsTrue(!cachedKeys.Contains("three"), "Three should be gone");
1177+
}
1178+
1179+
[Test]
1180+
public void ListOfCacheKeysContainsAllKeysAfterCallingGetOrAddAndRemovingOne()
1181+
{
1182+
List<string> keys = new List<string>() { "one", "two", "three", "four", "five" };
1183+
1184+
foreach (var key in keys)
1185+
{
1186+
sut.GetOrAdd(key, () => key.Reverse());
1187+
}
1188+
1189+
// remove three
1190+
sut.Remove("three");
1191+
1192+
var cachedKeys = sut.GetCacheKeys().Keys.ToList();
1193+
1194+
Assert.IsTrue(!cachedKeys.Contains("three"), "Three should be gone");
1195+
}
1196+
1197+
public void ListOfCacheKeysIsEmptyAfterRemovingThemAll()
1198+
{
1199+
List<string> keys = new List<string>() {"one", "two", "three", "four", "five"};
1200+
1201+
foreach (var key in keys)
1202+
{
1203+
sut.GetOrAdd(key, () => key.Reverse());
1204+
}
1205+
1206+
var cachedKeys = sut.GetCacheKeys().Keys.ToList();
1207+
Assert.IsTrue(keys.Intersect(cachedKeys).Count() == keys.Count);
1208+
1209+
foreach (var key in cachedKeys)
1210+
{
1211+
sut.Remove(key);
1212+
}
1213+
1214+
var cachedKeys2 = sut.GetCacheKeys().Keys.ToList();
1215+
Assert.AreEqual(cachedKeys2.Count, 0, "Should be zero keys left");
1216+
}
11281217
}
11291218
}

LazyCache/CachedItemMeta.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
using System;
2+
3+
namespace LazyCache
4+
{
5+
public class CachedItemMeta
6+
{
7+
public CachedItemMeta()
8+
{
9+
this.CreatedDate = DateTime.UtcNow;
10+
}
11+
12+
public DateTime CreatedDate { get; set; }
13+
}
14+
}

LazyCache/CachingService.cs

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
using System;
2+
using System.Collections.Concurrent;
23
using System.Collections.Generic;
4+
using System.Linq;
35
using System.Threading;
46
using System.Threading.Tasks;
57
using LazyCache.Providers;
@@ -15,6 +17,8 @@ public class CachingService : IAppCache
1517

1618
private readonly int[] keyLocks;
1719

20+
private ConcurrentDictionary<string, CachedItemMeta> cacheKeyDictionary;
21+
1822
public CachingService() : this(DefaultCacheProvider)
1923
{
2024
}
@@ -24,6 +28,7 @@ public CachingService(Lazy<ICacheProvider> cacheProvider)
2428
this.cacheProvider = cacheProvider ?? throw new ArgumentNullException(nameof(cacheProvider));
2529
var lockCount = Math.Max(Environment.ProcessorCount * 8, 32);
2630
keyLocks = new int[lockCount];
31+
cacheKeyDictionary = new ConcurrentDictionary<string, CachedItemMeta>();
2732
}
2833

2934
public CachingService(Func<ICacheProvider> cacheProviderFactory)
@@ -32,7 +37,7 @@ public CachingService(Func<ICacheProvider> cacheProviderFactory)
3237
cacheProvider = new Lazy<ICacheProvider>(cacheProviderFactory);
3338
var lockCount = Math.Max(Environment.ProcessorCount * 8, 32);
3439
keyLocks = new int[lockCount];
35-
40+
cacheKeyDictionary = new ConcurrentDictionary<string, CachedItemMeta>();
3641
}
3742

3843
public CachingService(ICacheProvider cache) : this(() => cache)
@@ -69,6 +74,7 @@ public virtual void Add<T>(string key, T item, MemoryCacheEntryOptions policy)
6974
ValidateKey(key);
7075

7176
CacheProvider.Set(key, item, policy);
77+
RememberCacheKey(key);
7278
}
7379

7480
public virtual T Get<T>(string key)
@@ -113,6 +119,7 @@ object CacheFactory(ICacheEntry entry) =>
113119
var result = addItemFactory(entry);
114120
SetAbsoluteExpirationFromRelative(entry);
115121
EnsureEvictionCallbackDoesNotReturnTheAsyncOrLazy<T>(entry.PostEvictionCallbacks);
122+
RememberCacheKey(entry.Key.ToString());
116123
return result;
117124
});
118125

@@ -137,6 +144,7 @@ object CacheFactory(ICacheEntry entry) =>
137144
if (valueHasChangedType)
138145
{
139146
CacheProvider.Remove(key);
147+
this.RemoveRememberedCacheKey(key);
140148

141149
// acquire lock again
142150
hash = (uint)key.GetHashCode() % (uint)keyLocks.Length;
@@ -158,6 +166,7 @@ object CacheFactory(ICacheEntry entry) =>
158166
catch //addItemFactory errored so do not cache the exception
159167
{
160168
CacheProvider.Remove(key);
169+
this.RemoveRememberedCacheKey(key);
161170
throw;
162171
}
163172
}
@@ -175,6 +184,7 @@ public virtual void Remove(string key)
175184
{
176185
ValidateKey(key);
177186
CacheProvider.Remove(key);
187+
RemoveRememberedCacheKey(key);
178188
}
179189

180190
public virtual ICacheProvider CacheProvider => cacheProvider.Value;
@@ -206,6 +216,7 @@ object CacheFactory(ICacheEntry entry) =>
206216
var result = addItemFactory(entry);
207217
SetAbsoluteExpirationFromRelative(entry);
208218
EnsureEvictionCallbackDoesNotReturnTheAsyncOrLazy<T>(entry.PostEvictionCallbacks);
219+
RememberCacheKey(entry.Key.ToString());
209220
return result;
210221
});
211222

@@ -226,6 +237,7 @@ object CacheFactory(ICacheEntry entry) =>
226237
if (valueHasChangedType)
227238
{
228239
CacheProvider.Remove(key);
240+
this.RemoveRememberedCacheKey(key);
229241

230242
// acquire lock
231243
hash = (uint)key.GetHashCode() % (uint)keyLocks.Length;
@@ -244,17 +256,26 @@ object CacheFactory(ICacheEntry entry) =>
244256

245257

246258
if (result.IsCanceled || result.IsFaulted)
259+
{
247260
CacheProvider.Remove(key);
261+
this.RemoveRememberedCacheKey(key);
262+
}
248263

249264
return await result.ConfigureAwait(false);
250265
}
251266
catch //addItemFactory errored so do not cache the exception
252267
{
253268
CacheProvider.Remove(key);
269+
this.RemoveRememberedCacheKey(key);
254270
throw;
255271
}
256272
}
257273

274+
public Dictionary<string, CachedItemMeta> GetCacheKeys()
275+
{
276+
return cacheKeyDictionary.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
277+
}
278+
258279
protected virtual T GetValueFromLazy<T>(object item, out bool valueHasChangedType)
259280
{
260281
valueHasChangedType = false;
@@ -342,5 +363,17 @@ protected virtual void ValidateKey(string key)
342363
if (string.IsNullOrWhiteSpace(key))
343364
throw new ArgumentOutOfRangeException(nameof(key), "Cache keys cannot be empty or whitespace");
344365
}
366+
367+
protected void RememberCacheKey(string key)
368+
{
369+
var meta = new CachedItemMeta();
370+
cacheKeyDictionary.AddOrUpdate(key, meta, (oldKey, oldValue) => meta);
371+
}
372+
373+
protected void RemoveRememberedCacheKey(string key)
374+
{
375+
CachedItemMeta remove;
376+
cacheKeyDictionary.TryRemove(key, out remove);
377+
}
345378
}
346379
}

LazyCache/IAppCache.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using System.Collections.Generic;
23
using System.Threading.Tasks;
34
using Microsoft.Extensions.Caching.Memory;
45

@@ -22,5 +23,6 @@ public interface IAppCache
2223
Task<T> GetOrAddAsync<T>(string key, Func<ICacheEntry, Task<T>> addItemFactory);
2324
Task<T> GetOrAddAsync<T>(string key, Func<ICacheEntry, Task<T>> addItemFactory, MemoryCacheEntryOptions policy);
2425
void Remove(string key);
26+
Dictionary<string, CachedItemMeta> GetCacheKeys();
2527
}
2628
}

LazyCache/Mocks/MockCachingService.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using System.Collections.Generic;
23
using System.Threading.Tasks;
34
using Microsoft.Extensions.Caching.Memory;
45

@@ -11,6 +12,7 @@ namespace LazyCache.Mocks
1112
public class MockCachingService : IAppCache
1213
{
1314
public ICacheProvider CacheProvider { get; } = new MockCacheProvider();
15+
1416
public CacheDefaults DefaultCachePolicy { get; set; } = new CacheDefaults();
1517

1618
public T Get<T>(string key)
@@ -57,5 +59,10 @@ public bool TryGetValue<T>(string key, out object value)
5759
value = default(T);
5860
return true;
5961
}
62+
63+
public Dictionary<string, CachedItemMeta> GetCacheKeys()
64+
{
65+
return new Dictionary<string, CachedItemMeta>();
66+
}
6067
}
6168
}

0 commit comments

Comments
 (0)