diff --git a/LazyCache.UnitTests/CachingServiceMemoryCacheProviderTests.cs b/LazyCache.UnitTests/CachingServiceMemoryCacheProviderTests.cs index 42aa3b9..f381820 100644 --- a/LazyCache.UnitTests/CachingServiceMemoryCacheProviderTests.cs +++ b/LazyCache.UnitTests/CachingServiceMemoryCacheProviderTests.cs @@ -32,7 +32,7 @@ private static CachingService BuildCache() private class ComplexTestObject { - public readonly IList SomeItems = new List {1, 2, 3, "testing123"}; + public readonly IList SomeItems = new List { 1, 2, 3, "testing123" }; public string SomeMessage = "testing123"; } @@ -528,7 +528,7 @@ public async Task Thread.Sleep(500); Assert.That(callbackValue, Is.AssignableTo>()); - var callbackResultValue = await (Task) callbackValue; + var callbackResultValue = await (Task)callbackValue; Assert.AreEqual(123, callbackResultValue); } @@ -873,7 +873,7 @@ MemoryCacheEntryOptions GetOptions() .SetAbsoluteExpiration(refreshInterval, ExpirationMode.ImmediateEviction); options.RegisterPostEvictionCallback((keyEvicted, value, reason, state) => { - if (reason == EvictionReason.Expired || reason == EvictionReason.TokenExpired) + if (reason == EvictionReason.Expired || reason == EvictionReason.TokenExpired) sut.GetOrAdd(key, _ => GetStuff(), GetOptions()); }); return options; @@ -946,7 +946,7 @@ ComplexTestObject GetStuff() { var key = $"stuff-{hits % uniqueCacheItems}"; var cached = await sut.GetOrAddAsync(key, () => GetStuffAsync(), DateTimeOffset.UtcNow.AddSeconds(1)); - if(!cancel.IsCancellationRequested) Interlocked.Increment(ref hits); + if (!cancel.IsCancellationRequested) Interlocked.Increment(ref hits); } }); }); @@ -1108,5 +1108,22 @@ public void RemovedItemCannotBeRetrievedFromCache() sut.Remove(TestKey); Assert.Null(sut.Get(TestKey)); } + + [Test] + public void TryGetReturnsCachedValueAndTrue() + { + string val = "Test Value"; + string key = "testkey"; + sut.Add(key, val); + + var contains = sut.TryGetValue(key, out var value); + + Assert.IsTrue(contains); + Assert.AreEqual(value, val); + + var contains2 = sut.TryGetValue("invalidkey", out var value2); + + Assert.IsFalse(contains2); + } } } \ No newline at end of file diff --git a/LazyCache/CachingService.cs b/LazyCache/CachingService.cs index 30b6754..6becdd3 100644 --- a/LazyCache/CachingService.cs +++ b/LazyCache/CachingService.cs @@ -41,7 +41,7 @@ public CachingService(ICacheProvider cache) : this(() => cache) } public static Lazy DefaultCacheProvider { get; set; } - = new Lazy(() => + = new Lazy(() => new MemoryCacheProvider( new MemoryCache( new MemoryCacheOptions()) @@ -89,6 +89,13 @@ public virtual Task GetAsync(string key) return GetValueFromAsyncLazy(item, out _); } + public virtual bool TryGetValue(string key, out object value) + { + ValidateKey(key); + + return CacheProvider.TryGetValue(key, out value); + } + public virtual T GetOrAdd(string key, Func addItemFactory) { return GetOrAdd(key, addItemFactory, null); @@ -112,7 +119,7 @@ object CacheFactory(ICacheEntry entry) => // acquire lock per key uint hash = (uint)key.GetHashCode() % (uint)keyLocks.Length; while (Interlocked.CompareExchange(ref keyLocks[hash], 1, 0) == 1) { Thread.Yield(); } - + try { cacheItem = CacheProvider.GetOrCreate(key, policy, CacheFactory); @@ -179,7 +186,7 @@ public virtual Task GetOrAddAsync(string key, Func> a public virtual async Task GetOrAddAsync(string key, Func> addItemFactory, MemoryCacheEntryOptions policy) - { + { ValidateKey(key); object cacheItem; @@ -280,7 +287,7 @@ protected virtual T GetValueFromLazy(object item, out bool valueHasChangedTyp protected virtual Task GetValueFromAsyncLazy(object item, out bool valueHasChangedType) { - valueHasChangedType = false; + valueHasChangedType = false; switch (item) { case AsyncLazy asyncLazy: diff --git a/LazyCache/IAppCache.cs b/LazyCache/IAppCache.cs index b171411..3d9133d 100644 --- a/LazyCache/IAppCache.cs +++ b/LazyCache/IAppCache.cs @@ -15,6 +15,8 @@ public interface IAppCache void Add(string key, T item, MemoryCacheEntryOptions policy); T Get(string key); Task GetAsync(string key); + bool TryGetValue(string key, out object value); + T GetOrAdd(string key, Func addItemFactory); T GetOrAdd(string key, Func addItemFactory, MemoryCacheEntryOptions policy); Task GetOrAddAsync(string key, Func> addItemFactory); diff --git a/LazyCache/ICacheProvider.cs b/LazyCache/ICacheProvider.cs index 83885d6..7a3ca0b 100644 --- a/LazyCache/ICacheProvider.cs +++ b/LazyCache/ICacheProvider.cs @@ -12,5 +12,6 @@ public interface ICacheProvider : IDisposable object GetOrCreate(string key, MemoryCacheEntryOptions policy, Func func); void Remove(string key); Task GetOrCreateAsync(string key, Func> func); + bool TryGetValue(object key, out object value); } } \ No newline at end of file diff --git a/LazyCache/Mocks/MockCacheProvider.cs b/LazyCache/Mocks/MockCacheProvider.cs index e8e2691..46bbeff 100644 --- a/LazyCache/Mocks/MockCacheProvider.cs +++ b/LazyCache/Mocks/MockCacheProvider.cs @@ -4,6 +4,7 @@ namespace LazyCache.Mocks { + public class MockCacheProvider : ICacheProvider { public void Set(string key, object item, MemoryCacheEntryOptions policy) @@ -34,6 +35,11 @@ public Task GetOrCreateAsync(string key, Func> func) return func(null); } + public bool TryGetValue(object key, out object value) + { + throw new NotImplementedException(); + } + public void Dispose() { } diff --git a/LazyCache/Mocks/MockCachingService.cs b/LazyCache/Mocks/MockCachingService.cs index 71016c9..9c9be86 100644 --- a/LazyCache/Mocks/MockCachingService.cs +++ b/LazyCache/Mocks/MockCachingService.cs @@ -51,5 +51,11 @@ public Task GetAsync(string key) public void Add(string key, T item, MemoryCacheEntryOptions policy) { } + + public bool TryGetValue(string key, out object value) + { + value = default(T); + return true; + } } } \ No newline at end of file diff --git a/LazyCache/Providers/MemoryCacheProvider.cs b/LazyCache/Providers/MemoryCacheProvider.cs index 3884c69..72739db 100644 --- a/LazyCache/Providers/MemoryCacheProvider.cs +++ b/LazyCache/Providers/MemoryCacheProvider.cs @@ -32,7 +32,7 @@ public object GetOrCreate(string key, Func factory) public object GetOrCreate(string key, MemoryCacheEntryOptions policy, Func factory) { - if(policy == null) + if (policy == null) return cache.GetOrCreate(key, factory); if (!cache.TryGetValue(key, out var result)) @@ -47,7 +47,7 @@ public object GetOrCreate(string key, MemoryCacheEntryOptions policy, Func + entry.RegisterPostEvictionCallback((keyPost, value, reason, state) => expiryTokenSource.Dispose()); result = factory(entry); @@ -78,6 +78,12 @@ public Task GetOrCreateAsync(string key, Func> factor return cache.GetOrCreateAsync(key, factory); } + public bool TryGetValue(object key, out object value) + { + return cache.TryGetValue(key, out value); + } + + public void Dispose() { cache?.Dispose();