From 33d055c75b455eacf62add2ca41182b541b47981 Mon Sep 17 00:00:00 2001 From: talmroth <talmroth@gmail.com> Date: Mon, 15 Mar 2021 12:26:01 -0700 Subject: [PATCH] Add TryGetValue (#142) Co-authored-by: Tom Almroth <TAlmroth@loandepot.com> --- .../CachingServiceMemoryCacheProviderTests.cs | 25 ++++++++++++++++--- LazyCache/CachingService.cs | 15 ++++++++--- LazyCache/IAppCache.cs | 2 ++ LazyCache/ICacheProvider.cs | 1 + LazyCache/Mocks/MockCacheProvider.cs | 6 +++++ LazyCache/Mocks/MockCachingService.cs | 6 +++++ LazyCache/Providers/MemoryCacheProvider.cs | 10 ++++++-- 7 files changed, 55 insertions(+), 10 deletions(-) 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<object> SomeItems = new List<object> {1, 2, 3, "testing123"}; + public readonly IList<object> SomeItems = new List<object> { 1, 2, 3, "testing123" }; public string SomeMessage = "testing123"; } @@ -528,7 +528,7 @@ public async Task Thread.Sleep(500); Assert.That(callbackValue, Is.AssignableTo<Task<int>>()); - var callbackResultValue = await (Task<int>) callbackValue; + var callbackResultValue = await (Task<int>)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<object>(TestKey)); } + + [Test] + public void TryGetReturnsCachedValueAndTrue() + { + string val = "Test Value"; + string key = "testkey"; + sut.Add(key, val); + + var contains = sut.TryGetValue<string>(key, out var value); + + Assert.IsTrue(contains); + Assert.AreEqual(value, val); + + var contains2 = sut.TryGetValue<string>("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<ICacheProvider> DefaultCacheProvider { get; set; } - = new Lazy<ICacheProvider>(() => + = new Lazy<ICacheProvider>(() => new MemoryCacheProvider( new MemoryCache( new MemoryCacheOptions()) @@ -89,6 +89,13 @@ public virtual Task<T> GetAsync<T>(string key) return GetValueFromAsyncLazy<T>(item, out _); } + public virtual bool TryGetValue<T>(string key, out object value) + { + ValidateKey(key); + + return CacheProvider.TryGetValue(key, out value); + } + public virtual T GetOrAdd<T>(string key, Func<ICacheEntry, T> 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<object>(key, policy, CacheFactory); @@ -179,7 +186,7 @@ public virtual Task<T> GetOrAddAsync<T>(string key, Func<ICacheEntry, Task<T>> a public virtual async Task<T> GetOrAddAsync<T>(string key, Func<ICacheEntry, Task<T>> addItemFactory, MemoryCacheEntryOptions policy) - { + { ValidateKey(key); object cacheItem; @@ -280,7 +287,7 @@ protected virtual T GetValueFromLazy<T>(object item, out bool valueHasChangedTyp protected virtual Task<T> GetValueFromAsyncLazy<T>(object item, out bool valueHasChangedType) { - valueHasChangedType = false; + valueHasChangedType = false; switch (item) { case AsyncLazy<T> 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<T>(string key, T item, MemoryCacheEntryOptions policy); T Get<T>(string key); Task<T> GetAsync<T>(string key); + bool TryGetValue<T>(string key, out object value); + T GetOrAdd<T>(string key, Func<ICacheEntry, T> addItemFactory); T GetOrAdd<T>(string key, Func<ICacheEntry, T> addItemFactory, MemoryCacheEntryOptions policy); Task<T> GetOrAddAsync<T>(string key, Func<ICacheEntry, Task<T>> 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<T>(string key, MemoryCacheEntryOptions policy, Func<ICacheEntry, T> func); void Remove(string key); Task<T> GetOrCreateAsync<T>(string key, Func<ICacheEntry, Task<T>> 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<T> GetOrCreateAsync<T>(string key, Func<ICacheEntry, Task<T>> 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<T> GetAsync<T>(string key) public void Add<T>(string key, T item, MemoryCacheEntryOptions policy) { } + + public bool TryGetValue<T>(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<T>(string key, Func<ICacheEntry, T> factory) public object GetOrCreate<T>(string key, MemoryCacheEntryOptions policy, Func<ICacheEntry, T> 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<T>(string key, MemoryCacheEntryOptions policy, Func<IC var expiryTokenSource = new CancellationTokenSource(); var expireToken = new CancellationChangeToken(expiryTokenSource.Token); entry.AddExpirationToken(expireToken); - entry.RegisterPostEvictionCallback((keyPost, value, reason, state) => + entry.RegisterPostEvictionCallback((keyPost, value, reason, state) => expiryTokenSource.Dispose()); result = factory(entry); @@ -78,6 +78,12 @@ public Task<T> GetOrCreateAsync<T>(string key, Func<ICacheEntry, Task<T>> 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();