From 4fc9fefb3a7ad73ed3b78ccb1c562cbcd2f55b2a Mon Sep 17 00:00:00 2001 From: Jonas Nyrup Date: Sun, 27 Sep 2020 10:55:51 +0200 Subject: [PATCH 1/5] Remove async-await pair (#134) --- LazyCache/CachingService.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/LazyCache/CachingService.cs b/LazyCache/CachingService.cs index 389ddcd..30b6754 100644 --- a/LazyCache/CachingService.cs +++ b/LazyCache/CachingService.cs @@ -172,10 +172,11 @@ public virtual void Remove(string key) public virtual ICacheProvider CacheProvider => cacheProvider.Value; - public virtual async Task GetOrAddAsync(string key, Func> addItemFactory) + public virtual Task GetOrAddAsync(string key, Func> addItemFactory) { - return await GetOrAddAsync(key, addItemFactory, null); + return GetOrAddAsync(key, addItemFactory, null); } + public virtual async Task GetOrAddAsync(string key, Func> addItemFactory, MemoryCacheEntryOptions policy) { From e38695bf63b1d33d97032e995d7dd5609dd692b3 Mon Sep 17 00:00:00 2001 From: Alastair Crabtree Date: Sun, 27 Sep 2020 09:57:53 +0100 Subject: [PATCH 2/5] Update ReleaseNotes.md --- ReleaseNotes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/ReleaseNotes.md b/ReleaseNotes.md index 53150ae..b2df8e6 100644 --- a/ReleaseNotes.md +++ b/ReleaseNotes.md @@ -3,6 +3,7 @@ ## Version 2.1.3 - Rename ExpirationMode.ImmediateExpiry => ExpirationMode.ImmediateEviction - Lovely new logo! (#133) Thanks to @doolali +- Performance imporvements to reduce allocations in #134 - thanks @jnyrup ## Version 2.1.2 - Tweak key lock array size based on CPU count so larger for bigger machines (See PR #126 and discussion with @jjxtra) From bba7f54057995c70272ce0830cab3065c58ce371 Mon Sep 17 00:00:00 2001 From: matthew-p Date: Sun, 27 Sep 2020 08:10:10 -0400 Subject: [PATCH 3/5] Issue72 Deprecate Add method in favor of Set --- .../CachingServiceMemoryCacheProviderTests.cs | 131 ++++++++++++++++++ LazyCache.UnitTests/DeprecationTests.cs | 44 ++++++ LazyCache/AppCacheExtensions.cs | 36 +++-- LazyCache/CachingService.cs | 34 +++-- LazyCache/IAppCache.cs | 2 + LazyCache/Mocks/MockCachingService.cs | 6 + 6 files changed, 230 insertions(+), 23 deletions(-) create mode 100644 LazyCache.UnitTests/DeprecationTests.cs diff --git a/LazyCache.UnitTests/CachingServiceMemoryCacheProviderTests.cs b/LazyCache.UnitTests/CachingServiceMemoryCacheProviderTests.cs index 42aa3b9..9738768 100644 --- a/LazyCache.UnitTests/CachingServiceMemoryCacheProviderTests.cs +++ b/LazyCache.UnitTests/CachingServiceMemoryCacheProviderTests.cs @@ -177,6 +177,137 @@ public void AddWithSlidingThatExpiresReturnsNull() Assert.IsNull(sut.Get(TestKey)); } + [Test] + public void SetComplexObjectThenGetGenericReturnsCachedObject() + { + testObject.SomeItems.Add("Another"); + testObject.SomeMessage = "changed-it-up"; + sut.Set(TestKey, testObject); + var actual = sut.Get(TestKey); + var expected = testObject; + Assert.NotNull(actual); + Assert.AreEqual(expected, actual); + testObject.SomeItems.Should().Contain("Another"); + testObject.SomeMessage.Should().Be("changed-it-up"); + } + + [Test] + public void SetComplexObjectThenGetReturnsCachedObject() + { + sut.Set(TestKey, testObject); + var actual = sut.Get(TestKey) as ComplexTestObject; + var expected = testObject; + Assert.NotNull(actual); + Assert.AreEqual(expected, actual); + } + + [Test] + public void SetEmptyKeyThrowsException() + { + Action act = () => sut.Set("", new object()); + act.Should().Throw(); + } + + [Test] + public void SetEmptyKeyThrowsExceptionWithExpiration() + { + Action act = () => sut.Set("", new object(), DateTimeOffset.Now.AddHours(1)); + act.Should().Throw(); + } + + [Test] + public void SetEmptyKeyThrowsExceptionWithPolicy() + { + Action act = () => sut.Set("", new object(), new MemoryCacheEntryOptions()); + act.Should().Throw(); + } + + [Test] + public void SetEmptyKeyThrowsExceptionWithSliding() + { + Action act = () => sut.Set("", new object(), new TimeSpan(1000)); + act.Should().Throw(); + } + + [Test] + public void SetNullKeyThrowsException() + { + Action act = () => sut.Set(null, new object()); + act.Should().Throw(); + } + + [Test] + public void SetNullKeyThrowsExceptionWithExpiration() + { + Action act = () => sut.Set(null, new object(), DateTimeOffset.Now.AddHours(1)); + act.Should().Throw(); + } + + [Test] + public void SetNullKeyThrowsExceptionWithPolicy() + { + Action act = () => sut.Set(null, new object(), new MemoryCacheEntryOptions()); + act.Should().Throw(); + } + + [Test] + public void SetNullKeyThrowsExceptionWithSliding() + { + Action act = () => sut.Set(null, new object(), new TimeSpan(1000)); + act.Should().Throw(); + } + + [Test] + public void SetNullThrowsException() + { + Action act = () => sut.Add(TestKey, null); + act.Should().Throw(); + } + + [Test] + public void SetThenGetReturnsCachedObject() + { + sut.Set(TestKey, "testObject"); + Assert.AreEqual("testObject", sut.Get(TestKey)); + } + + [Test] + public void SetWithOffsetReturnsCachedItem() + { + sut.Set(TestKey, "testObject", DateTimeOffset.Now.AddSeconds(1)); + Assert.AreEqual("testObject", sut.Get(TestKey)); + } + + [Test] + public void SetWithOffsetThatExpiresReturnsNull() + { + sut.Set(TestKey, "testObject", DateTimeOffset.Now.AddSeconds(1)); + Thread.Sleep(1500); + Assert.IsNull(sut.Get(TestKey)); + } + + [Test] + public void SetWithPolicyReturnsCachedItem() + { + sut.Set(TestKey, "testObject", new MemoryCacheEntryOptions()); + Assert.AreEqual("testObject", sut.Get(TestKey)); + } + + [Test] + public void SetWithSlidingReturnsCachedItem() + { + sut.Set(TestKey, "testObject", new TimeSpan(5000)); + Assert.AreEqual("testObject", sut.Get(TestKey)); + } + + [Test] + public void SetWithSlidingThatExpiresReturnsNull() + { + sut.Set(TestKey, "testObject", new TimeSpan(750)); + Thread.Sleep(1500); + Assert.IsNull(sut.Get(TestKey)); + } + [Test] public void CacheProviderIsNotNull() { diff --git a/LazyCache.UnitTests/DeprecationTests.cs b/LazyCache.UnitTests/DeprecationTests.cs new file mode 100644 index 0000000..29ddcc6 --- /dev/null +++ b/LazyCache.UnitTests/DeprecationTests.cs @@ -0,0 +1,44 @@ +using NUnit.Framework; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; + +namespace LazyCache.UnitTests +{ + public class DeprecationTests + { + [Test] + public void AllImplementationsOfIAppCache_HaveTheAddMethod_MarkedAsObsolete() + { + var classesWithoutObsoleteTag = new List(); + + foreach (var type in GetTypesWithIAppCache()) + { + if (MethodDoesNotHaveObsoleteAttribute(type)) + { + classesWithoutObsoleteTag.Add(type); + } + } + CollectionAssert.IsEmpty(classesWithoutObsoleteTag); + } + + private IEnumerable GetTypesWithIAppCache() + { + var iAppCache = typeof(IAppCache); + return Assembly.GetAssembly(iAppCache) + .GetTypes() + .Where(iAppCache.IsAssignableFrom); + } + + private bool MethodDoesNotHaveObsoleteAttribute(Type type) + { + var method = type.GetMethods() + .SingleOrDefault(m => m.Name == nameof(IAppCache.Add)); + var attribute = method?.GetCustomAttributes(typeof(ObsoleteAttribute), true) + .SingleOrDefault() as ObsoleteAttribute; + return attribute == null; + } + + } +} \ No newline at end of file diff --git a/LazyCache/AppCacheExtensions.cs b/LazyCache/AppCacheExtensions.cs index 9d4a823..854695f 100644 --- a/LazyCache/AppCacheExtensions.cs +++ b/LazyCache/AppCacheExtensions.cs @@ -8,25 +8,22 @@ namespace LazyCache { public static class AppCacheExtensions { + [Obsolete("This method has been deprecated. Use Set instead.", false)] public static void Add(this IAppCache cache, string key, T item) { - if (cache == null) throw new ArgumentNullException(nameof(cache)); - - cache.Add(key, item, cache.DefaultCachePolicy.BuildOptions()); + Set(cache, key, item); } + [Obsolete("This method has been deprecated. Use Set instead.", false)] public static void Add(this IAppCache cache, string key, T item, DateTimeOffset expires) { - if (cache == null) throw new ArgumentNullException(nameof(cache)); - - cache.Add(key, item, new MemoryCacheEntryOptions {AbsoluteExpiration = expires}); + Set(cache, key, item, expires); } + [Obsolete("This method has been deprecated. Use Set instead.", false)] public static void Add(this IAppCache cache, string key, T item, TimeSpan slidingExpiration) { - if (cache == null) throw new ArgumentNullException(nameof(cache)); - - cache.Add(key, item, new MemoryCacheEntryOptions {SlidingExpiration = slidingExpiration}); + Set(cache, key, item, slidingExpiration); } public static T GetOrAdd(this IAppCache cache, string key, Func addItemFactory) @@ -116,5 +113,26 @@ public static Task GetOrAddAsync(this IAppCache cache, string key, Func addItemFactory(), policy); } + + public static void Set(this IAppCache cache, string key, T item) + { + if (cache == null) throw new ArgumentNullException(nameof(cache)); + + cache.Set(key, item, cache.DefaultCachePolicy.BuildOptions()); + } + + public static void Set(this IAppCache cache, string key, T item, DateTimeOffset expires) + { + if (cache == null) throw new ArgumentNullException(nameof(cache)); + + cache.Set(key, item, new MemoryCacheEntryOptions { AbsoluteExpiration = expires }); + } + + public static void Set(this IAppCache cache, string key, T item, TimeSpan slidingExpiration) + { + if (cache == null) throw new ArgumentNullException(nameof(cache)); + + cache.Set(key, item, new MemoryCacheEntryOptions { SlidingExpiration = slidingExpiration }); + } } } \ No newline at end of file diff --git a/LazyCache/CachingService.cs b/LazyCache/CachingService.cs index 389ddcd..981928c 100644 --- a/LazyCache/CachingService.cs +++ b/LazyCache/CachingService.cs @@ -62,13 +62,10 @@ public virtual int DefaultCacheDuration /// public virtual CacheDefaults DefaultCachePolicy { get; set; } = new CacheDefaults(); + [Obsolete("This method has been deprecated. Use Set instead.", false)] public virtual void Add(string key, T item, MemoryCacheEntryOptions policy) { - if (item == null) - throw new ArgumentNullException(nameof(item)); - ValidateKey(key); - - CacheProvider.Set(key, item, policy); + Set(key, item, policy); } public virtual T Get(string key) @@ -155,15 +152,6 @@ object CacheFactory(ICacheEntry entry) => } } - private static void SetAbsoluteExpirationFromRelative(ICacheEntry entry) - { - if (!entry.AbsoluteExpirationRelativeToNow.HasValue) return; - - var absoluteExpiration = DateTimeOffset.UtcNow + entry.AbsoluteExpirationRelativeToNow.Value; - if (!entry.AbsoluteExpiration.HasValue || absoluteExpiration < entry.AbsoluteExpiration) - entry.AbsoluteExpiration = absoluteExpiration; - } - public virtual void Remove(string key) { ValidateKey(key); @@ -247,6 +235,15 @@ object CacheFactory(ICacheEntry entry) => } } + public virtual void Set(string key, T item, MemoryCacheEntryOptions policy) + { + if (item == null) + throw new ArgumentNullException(nameof(item)); + ValidateKey(key); + + CacheProvider.Set(key, item, policy); + } + protected virtual T GetValueFromLazy(object item, out bool valueHasChangedType) { valueHasChangedType = false; @@ -334,5 +331,14 @@ protected virtual void ValidateKey(string key) if (string.IsNullOrWhiteSpace(key)) throw new ArgumentOutOfRangeException(nameof(key), "Cache keys cannot be empty or whitespace"); } + + private static void SetAbsoluteExpirationFromRelative(ICacheEntry entry) + { + if (!entry.AbsoluteExpirationRelativeToNow.HasValue) return; + + var absoluteExpiration = DateTimeOffset.UtcNow + entry.AbsoluteExpirationRelativeToNow.Value; + if (!entry.AbsoluteExpiration.HasValue || absoluteExpiration < entry.AbsoluteExpiration) + entry.AbsoluteExpiration = absoluteExpiration; + } } } \ No newline at end of file diff --git a/LazyCache/IAppCache.cs b/LazyCache/IAppCache.cs index b171411..07dd250 100644 --- a/LazyCache/IAppCache.cs +++ b/LazyCache/IAppCache.cs @@ -12,6 +12,7 @@ public interface IAppCache /// Define the number of seconds to cache objects for by default /// CacheDefaults DefaultCachePolicy { get; } + [Obsolete("This method has been deprecated. Use Set instead.", false)] void Add(string key, T item, MemoryCacheEntryOptions policy); T Get(string key); Task GetAsync(string key); @@ -20,5 +21,6 @@ public interface IAppCache Task GetOrAddAsync(string key, Func> addItemFactory); Task GetOrAddAsync(string key, Func> addItemFactory, MemoryCacheEntryOptions policy); void Remove(string key); + void Set(string key, T item, MemoryCacheEntryOptions policy); } } \ No newline at end of file diff --git a/LazyCache/Mocks/MockCachingService.cs b/LazyCache/Mocks/MockCachingService.cs index 71016c9..dd49c7c 100644 --- a/LazyCache/Mocks/MockCachingService.cs +++ b/LazyCache/Mocks/MockCachingService.cs @@ -48,7 +48,13 @@ public Task GetAsync(string key) return Task.FromResult(default(T)); } + [Obsolete("This method has been deprecated. Use Set instead.", false)] public void Add(string key, T item, MemoryCacheEntryOptions policy) + { + Set(key, item, policy); + } + + public void Set(string key, T item, MemoryCacheEntryOptions policy) { } } From 858bd6d4d4d41e18be34bb979f9f3751628da084 Mon Sep 17 00:00:00 2001 From: matthew-p Date: Sun, 27 Sep 2020 08:10:10 -0400 Subject: [PATCH 4/5] Issue72 Deprecate Add method in favor of Set --- .../CachingServiceMemoryCacheProviderTests.cs | 131 ++++++++++++++++++ LazyCache.UnitTests/DeprecationTests.cs | 44 ++++++ LazyCache/AppCacheExtensions.cs | 36 +++-- LazyCache/CachingService.cs | 34 +++-- LazyCache/IAppCache.cs | 2 + LazyCache/Mocks/MockCachingService.cs | 6 + 6 files changed, 230 insertions(+), 23 deletions(-) create mode 100644 LazyCache.UnitTests/DeprecationTests.cs diff --git a/LazyCache.UnitTests/CachingServiceMemoryCacheProviderTests.cs b/LazyCache.UnitTests/CachingServiceMemoryCacheProviderTests.cs index 42aa3b9..9738768 100644 --- a/LazyCache.UnitTests/CachingServiceMemoryCacheProviderTests.cs +++ b/LazyCache.UnitTests/CachingServiceMemoryCacheProviderTests.cs @@ -177,6 +177,137 @@ public void AddWithSlidingThatExpiresReturnsNull() Assert.IsNull(sut.Get(TestKey)); } + [Test] + public void SetComplexObjectThenGetGenericReturnsCachedObject() + { + testObject.SomeItems.Add("Another"); + testObject.SomeMessage = "changed-it-up"; + sut.Set(TestKey, testObject); + var actual = sut.Get(TestKey); + var expected = testObject; + Assert.NotNull(actual); + Assert.AreEqual(expected, actual); + testObject.SomeItems.Should().Contain("Another"); + testObject.SomeMessage.Should().Be("changed-it-up"); + } + + [Test] + public void SetComplexObjectThenGetReturnsCachedObject() + { + sut.Set(TestKey, testObject); + var actual = sut.Get(TestKey) as ComplexTestObject; + var expected = testObject; + Assert.NotNull(actual); + Assert.AreEqual(expected, actual); + } + + [Test] + public void SetEmptyKeyThrowsException() + { + Action act = () => sut.Set("", new object()); + act.Should().Throw(); + } + + [Test] + public void SetEmptyKeyThrowsExceptionWithExpiration() + { + Action act = () => sut.Set("", new object(), DateTimeOffset.Now.AddHours(1)); + act.Should().Throw(); + } + + [Test] + public void SetEmptyKeyThrowsExceptionWithPolicy() + { + Action act = () => sut.Set("", new object(), new MemoryCacheEntryOptions()); + act.Should().Throw(); + } + + [Test] + public void SetEmptyKeyThrowsExceptionWithSliding() + { + Action act = () => sut.Set("", new object(), new TimeSpan(1000)); + act.Should().Throw(); + } + + [Test] + public void SetNullKeyThrowsException() + { + Action act = () => sut.Set(null, new object()); + act.Should().Throw(); + } + + [Test] + public void SetNullKeyThrowsExceptionWithExpiration() + { + Action act = () => sut.Set(null, new object(), DateTimeOffset.Now.AddHours(1)); + act.Should().Throw(); + } + + [Test] + public void SetNullKeyThrowsExceptionWithPolicy() + { + Action act = () => sut.Set(null, new object(), new MemoryCacheEntryOptions()); + act.Should().Throw(); + } + + [Test] + public void SetNullKeyThrowsExceptionWithSliding() + { + Action act = () => sut.Set(null, new object(), new TimeSpan(1000)); + act.Should().Throw(); + } + + [Test] + public void SetNullThrowsException() + { + Action act = () => sut.Add(TestKey, null); + act.Should().Throw(); + } + + [Test] + public void SetThenGetReturnsCachedObject() + { + sut.Set(TestKey, "testObject"); + Assert.AreEqual("testObject", sut.Get(TestKey)); + } + + [Test] + public void SetWithOffsetReturnsCachedItem() + { + sut.Set(TestKey, "testObject", DateTimeOffset.Now.AddSeconds(1)); + Assert.AreEqual("testObject", sut.Get(TestKey)); + } + + [Test] + public void SetWithOffsetThatExpiresReturnsNull() + { + sut.Set(TestKey, "testObject", DateTimeOffset.Now.AddSeconds(1)); + Thread.Sleep(1500); + Assert.IsNull(sut.Get(TestKey)); + } + + [Test] + public void SetWithPolicyReturnsCachedItem() + { + sut.Set(TestKey, "testObject", new MemoryCacheEntryOptions()); + Assert.AreEqual("testObject", sut.Get(TestKey)); + } + + [Test] + public void SetWithSlidingReturnsCachedItem() + { + sut.Set(TestKey, "testObject", new TimeSpan(5000)); + Assert.AreEqual("testObject", sut.Get(TestKey)); + } + + [Test] + public void SetWithSlidingThatExpiresReturnsNull() + { + sut.Set(TestKey, "testObject", new TimeSpan(750)); + Thread.Sleep(1500); + Assert.IsNull(sut.Get(TestKey)); + } + [Test] public void CacheProviderIsNotNull() { diff --git a/LazyCache.UnitTests/DeprecationTests.cs b/LazyCache.UnitTests/DeprecationTests.cs new file mode 100644 index 0000000..29ddcc6 --- /dev/null +++ b/LazyCache.UnitTests/DeprecationTests.cs @@ -0,0 +1,44 @@ +using NUnit.Framework; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; + +namespace LazyCache.UnitTests +{ + public class DeprecationTests + { + [Test] + public void AllImplementationsOfIAppCache_HaveTheAddMethod_MarkedAsObsolete() + { + var classesWithoutObsoleteTag = new List(); + + foreach (var type in GetTypesWithIAppCache()) + { + if (MethodDoesNotHaveObsoleteAttribute(type)) + { + classesWithoutObsoleteTag.Add(type); + } + } + CollectionAssert.IsEmpty(classesWithoutObsoleteTag); + } + + private IEnumerable GetTypesWithIAppCache() + { + var iAppCache = typeof(IAppCache); + return Assembly.GetAssembly(iAppCache) + .GetTypes() + .Where(iAppCache.IsAssignableFrom); + } + + private bool MethodDoesNotHaveObsoleteAttribute(Type type) + { + var method = type.GetMethods() + .SingleOrDefault(m => m.Name == nameof(IAppCache.Add)); + var attribute = method?.GetCustomAttributes(typeof(ObsoleteAttribute), true) + .SingleOrDefault() as ObsoleteAttribute; + return attribute == null; + } + + } +} \ No newline at end of file diff --git a/LazyCache/AppCacheExtensions.cs b/LazyCache/AppCacheExtensions.cs index 9d4a823..854695f 100644 --- a/LazyCache/AppCacheExtensions.cs +++ b/LazyCache/AppCacheExtensions.cs @@ -8,25 +8,22 @@ namespace LazyCache { public static class AppCacheExtensions { + [Obsolete("This method has been deprecated. Use Set instead.", false)] public static void Add(this IAppCache cache, string key, T item) { - if (cache == null) throw new ArgumentNullException(nameof(cache)); - - cache.Add(key, item, cache.DefaultCachePolicy.BuildOptions()); + Set(cache, key, item); } + [Obsolete("This method has been deprecated. Use Set instead.", false)] public static void Add(this IAppCache cache, string key, T item, DateTimeOffset expires) { - if (cache == null) throw new ArgumentNullException(nameof(cache)); - - cache.Add(key, item, new MemoryCacheEntryOptions {AbsoluteExpiration = expires}); + Set(cache, key, item, expires); } + [Obsolete("This method has been deprecated. Use Set instead.", false)] public static void Add(this IAppCache cache, string key, T item, TimeSpan slidingExpiration) { - if (cache == null) throw new ArgumentNullException(nameof(cache)); - - cache.Add(key, item, new MemoryCacheEntryOptions {SlidingExpiration = slidingExpiration}); + Set(cache, key, item, slidingExpiration); } public static T GetOrAdd(this IAppCache cache, string key, Func addItemFactory) @@ -116,5 +113,26 @@ public static Task GetOrAddAsync(this IAppCache cache, string key, Func addItemFactory(), policy); } + + public static void Set(this IAppCache cache, string key, T item) + { + if (cache == null) throw new ArgumentNullException(nameof(cache)); + + cache.Set(key, item, cache.DefaultCachePolicy.BuildOptions()); + } + + public static void Set(this IAppCache cache, string key, T item, DateTimeOffset expires) + { + if (cache == null) throw new ArgumentNullException(nameof(cache)); + + cache.Set(key, item, new MemoryCacheEntryOptions { AbsoluteExpiration = expires }); + } + + public static void Set(this IAppCache cache, string key, T item, TimeSpan slidingExpiration) + { + if (cache == null) throw new ArgumentNullException(nameof(cache)); + + cache.Set(key, item, new MemoryCacheEntryOptions { SlidingExpiration = slidingExpiration }); + } } } \ No newline at end of file diff --git a/LazyCache/CachingService.cs b/LazyCache/CachingService.cs index 30b6754..29907f8 100644 --- a/LazyCache/CachingService.cs +++ b/LazyCache/CachingService.cs @@ -62,13 +62,10 @@ public virtual int DefaultCacheDuration /// public virtual CacheDefaults DefaultCachePolicy { get; set; } = new CacheDefaults(); + [Obsolete("This method has been deprecated. Use Set instead.", false)] public virtual void Add(string key, T item, MemoryCacheEntryOptions policy) { - if (item == null) - throw new ArgumentNullException(nameof(item)); - ValidateKey(key); - - CacheProvider.Set(key, item, policy); + Set(key, item, policy); } public virtual T Get(string key) @@ -155,15 +152,6 @@ object CacheFactory(ICacheEntry entry) => } } - private static void SetAbsoluteExpirationFromRelative(ICacheEntry entry) - { - if (!entry.AbsoluteExpirationRelativeToNow.HasValue) return; - - var absoluteExpiration = DateTimeOffset.UtcNow + entry.AbsoluteExpirationRelativeToNow.Value; - if (!entry.AbsoluteExpiration.HasValue || absoluteExpiration < entry.AbsoluteExpiration) - entry.AbsoluteExpiration = absoluteExpiration; - } - public virtual void Remove(string key) { ValidateKey(key); @@ -248,6 +236,15 @@ object CacheFactory(ICacheEntry entry) => } } + public virtual void Set(string key, T item, MemoryCacheEntryOptions policy) + { + if (item == null) + throw new ArgumentNullException(nameof(item)); + ValidateKey(key); + + CacheProvider.Set(key, item, policy); + } + protected virtual T GetValueFromLazy(object item, out bool valueHasChangedType) { valueHasChangedType = false; @@ -335,5 +332,14 @@ protected virtual void ValidateKey(string key) if (string.IsNullOrWhiteSpace(key)) throw new ArgumentOutOfRangeException(nameof(key), "Cache keys cannot be empty or whitespace"); } + + private static void SetAbsoluteExpirationFromRelative(ICacheEntry entry) + { + if (!entry.AbsoluteExpirationRelativeToNow.HasValue) return; + + var absoluteExpiration = DateTimeOffset.UtcNow + entry.AbsoluteExpirationRelativeToNow.Value; + if (!entry.AbsoluteExpiration.HasValue || absoluteExpiration < entry.AbsoluteExpiration) + entry.AbsoluteExpiration = absoluteExpiration; + } } } \ No newline at end of file diff --git a/LazyCache/IAppCache.cs b/LazyCache/IAppCache.cs index b171411..07dd250 100644 --- a/LazyCache/IAppCache.cs +++ b/LazyCache/IAppCache.cs @@ -12,6 +12,7 @@ public interface IAppCache /// Define the number of seconds to cache objects for by default /// CacheDefaults DefaultCachePolicy { get; } + [Obsolete("This method has been deprecated. Use Set instead.", false)] void Add(string key, T item, MemoryCacheEntryOptions policy); T Get(string key); Task GetAsync(string key); @@ -20,5 +21,6 @@ public interface IAppCache Task GetOrAddAsync(string key, Func> addItemFactory); Task GetOrAddAsync(string key, Func> addItemFactory, MemoryCacheEntryOptions policy); void Remove(string key); + void Set(string key, T item, MemoryCacheEntryOptions policy); } } \ No newline at end of file diff --git a/LazyCache/Mocks/MockCachingService.cs b/LazyCache/Mocks/MockCachingService.cs index 71016c9..dd49c7c 100644 --- a/LazyCache/Mocks/MockCachingService.cs +++ b/LazyCache/Mocks/MockCachingService.cs @@ -48,7 +48,13 @@ public Task GetAsync(string key) return Task.FromResult(default(T)); } + [Obsolete("This method has been deprecated. Use Set instead.", false)] public void Add(string key, T item, MemoryCacheEntryOptions policy) + { + Set(key, item, policy); + } + + public void Set(string key, T item, MemoryCacheEntryOptions policy) { } } From 43b0726fa96962a7446994ee4349978b9afb4f27 Mon Sep 17 00:00:00 2001 From: matthew-p Date: Sat, 10 Oct 2020 16:05:45 -0400 Subject: [PATCH 5/5] Issue72 add NeverBrowsableState to Add() --- LazyCache.UnitTests/DeprecationTests.cs | 64 ++++++++++++++++++++++++- LazyCache/AppCacheExtensions.cs | 4 ++ LazyCache/CachingService.cs | 2 + LazyCache/IAppCache.cs | 2 + LazyCache/Mocks/MockCachingService.cs | 2 + 5 files changed, 73 insertions(+), 1 deletion(-) diff --git a/LazyCache.UnitTests/DeprecationTests.cs b/LazyCache.UnitTests/DeprecationTests.cs index 29ddcc6..2bd9028 100644 --- a/LazyCache.UnitTests/DeprecationTests.cs +++ b/LazyCache.UnitTests/DeprecationTests.cs @@ -1,11 +1,15 @@ using NUnit.Framework; using System; using System.Collections.Generic; +using System.ComponentModel; using System.Linq; using System.Reflection; namespace LazyCache.UnitTests { + /// + /// Tests to confirm that methods intended to be deprecated have been adequately marked as such + /// public class DeprecationTests { [Test] @@ -20,7 +24,52 @@ public void AllImplementationsOfIAppCache_HaveTheAddMethod_MarkedAsObsolete() classesWithoutObsoleteTag.Add(type); } } - CollectionAssert.IsEmpty(classesWithoutObsoleteTag); + CollectionAssert.IsEmpty(classesWithoutObsoleteTag, "Types which do not have the Add() method marked as Obsolete"); + } + + [Test] + public void AllImplementationsOfIAppCache_HaveTheAddMethod_MarkedAsNeverBrowsable() + { + var classesWhereAddAppearsBrowsable = new List(); + + foreach (var type in GetTypesWithIAppCache()) + { + var editorBrowsableState = GetAddMethodsEditorBrowsableState(type); + if (editorBrowsableState != EditorBrowsableState.Never) + { + classesWhereAddAppearsBrowsable.Add(type); + } + } + CollectionAssert.IsEmpty(classesWhereAddAppearsBrowsable, "Types which have the Add() method not marked as Never browsable"); + } + + [Test] + public void AppCacheExtensions_AddMethods_AreObsolete() + { + var addMethodsWithoutObsoleteAttribute = + typeof(AppCacheExtensions).GetMethods() + .Where(m => m.Name == nameof(AppCacheExtensions.Add) + && m.GetCustomAttribute(typeof(ObsoleteAttribute), true) == null); + + CollectionAssert.IsEmpty(addMethodsWithoutObsoleteAttribute, "Add methods not marked as obsolete"); + } + + [Test] + public void AppCacheExtensions_AddMethods_AreNotBrowsable() + { + var addMethodsThatAreBrowsable = + typeof(AppCacheExtensions) + .GetMethods() + .Where(m => m.Name == nameof(AppCacheExtensions.Add)) + .Select(m => (Method: m, Attribute: GetEditorBrowsableAttribute(m))) + .Where(ma => ma.Attribute.State != EditorBrowsableState.Never); + + CollectionAssert.IsEmpty(addMethodsThatAreBrowsable.Select(ma => ma.Method), "Add methods not marked as Never browsable"); + + static EditorBrowsableAttribute GetEditorBrowsableAttribute(MethodInfo m) + { + return m.GetCustomAttributes(typeof(EditorBrowsableAttribute), true).Single() as EditorBrowsableAttribute; + } } private IEnumerable GetTypesWithIAppCache() @@ -40,5 +89,18 @@ private bool MethodDoesNotHaveObsoleteAttribute(Type type) return attribute == null; } + private EditorBrowsableState GetAddMethodsEditorBrowsableState(Type type) + { + var addMethod = nameof(IAppCache.Add); + var method = type.GetMethods() + .SingleOrDefault(m => m.Name == addMethod); + var attribute = method?.GetCustomAttributes(typeof(EditorBrowsableAttribute), true) + .SingleOrDefault() as EditorBrowsableAttribute; + if (attribute == null) + { + throw new InvalidOperationException($"{type.Name}'s {addMethod} method does not have {nameof(EditorBrowsableAttribute)}"); + } + return attribute.State; + } } } \ No newline at end of file diff --git a/LazyCache/AppCacheExtensions.cs b/LazyCache/AppCacheExtensions.cs index 854695f..bf28b81 100644 --- a/LazyCache/AppCacheExtensions.cs +++ b/LazyCache/AppCacheExtensions.cs @@ -1,4 +1,5 @@ using System; +using System.ComponentModel; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Caching.Memory; @@ -9,18 +10,21 @@ namespace LazyCache public static class AppCacheExtensions { [Obsolete("This method has been deprecated. Use Set instead.", false)] + [EditorBrowsable(EditorBrowsableState.Never)] public static void Add(this IAppCache cache, string key, T item) { Set(cache, key, item); } [Obsolete("This method has been deprecated. Use Set instead.", false)] + [EditorBrowsable(EditorBrowsableState.Never)] public static void Add(this IAppCache cache, string key, T item, DateTimeOffset expires) { Set(cache, key, item, expires); } [Obsolete("This method has been deprecated. Use Set instead.", false)] + [EditorBrowsable(EditorBrowsableState.Never)] public static void Add(this IAppCache cache, string key, T item, TimeSpan slidingExpiration) { Set(cache, key, item, slidingExpiration); diff --git a/LazyCache/CachingService.cs b/LazyCache/CachingService.cs index 29907f8..581ae09 100644 --- a/LazyCache/CachingService.cs +++ b/LazyCache/CachingService.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.ComponentModel; using System.Threading; using System.Threading.Tasks; using LazyCache.Providers; @@ -63,6 +64,7 @@ public virtual int DefaultCacheDuration public virtual CacheDefaults DefaultCachePolicy { get; set; } = new CacheDefaults(); [Obsolete("This method has been deprecated. Use Set instead.", false)] + [EditorBrowsable(EditorBrowsableState.Never)] public virtual void Add(string key, T item, MemoryCacheEntryOptions policy) { Set(key, item, policy); diff --git a/LazyCache/IAppCache.cs b/LazyCache/IAppCache.cs index 07dd250..caad282 100644 --- a/LazyCache/IAppCache.cs +++ b/LazyCache/IAppCache.cs @@ -1,4 +1,5 @@ using System; +using System.ComponentModel; using System.Threading.Tasks; using Microsoft.Extensions.Caching.Memory; @@ -13,6 +14,7 @@ public interface IAppCache /// CacheDefaults DefaultCachePolicy { get; } [Obsolete("This method has been deprecated. Use Set instead.", false)] + [EditorBrowsable(EditorBrowsableState.Never)] void Add(string key, T item, MemoryCacheEntryOptions policy); T Get(string key); Task GetAsync(string key); diff --git a/LazyCache/Mocks/MockCachingService.cs b/LazyCache/Mocks/MockCachingService.cs index dd49c7c..5c8210f 100644 --- a/LazyCache/Mocks/MockCachingService.cs +++ b/LazyCache/Mocks/MockCachingService.cs @@ -1,4 +1,5 @@ using System; +using System.ComponentModel; using System.Threading.Tasks; using Microsoft.Extensions.Caching.Memory; @@ -49,6 +50,7 @@ public Task GetAsync(string key) } [Obsolete("This method has been deprecated. Use Set instead.", false)] + [EditorBrowsable(EditorBrowsableState.Never)] public void Add(string key, T item, MemoryCacheEntryOptions policy) { Set(key, item, policy);