From 6ddd50d7acbb12c4e3ecc78acf41cd192a140b6c Mon Sep 17 00:00:00 2001 From: catcherwong Date: Sat, 5 Jan 2019 19:42:16 +0800 Subject: [PATCH 1/3] :sparkles: TrySet #64 --- build/version.props | 2 +- src/EasyCaching.Core/IEasyCachingProvider.cs | 20 ++++++ .../HybridCachingProvider.cs | 10 +++ .../DefaultInMemoryCachingProvider.cs | 58 +++++++++++++++++ .../DefaultMemcachedCachingProvider.cs | 46 ++++++++++++++ .../DefaultRedisCachingProvider.cs | 56 +++++++++++++++++ src/EasyCaching.SQLite/ConstSQL.cs | 14 +++++ .../DefaultSQLiteCachingProvider.cs | 62 +++++++++++++++++++ .../CachingTests/BaseCachingProviderTest.cs | 37 +++++++++++ .../CachingTests/HybridCachingTest.cs | 13 ++++ 10 files changed, 317 insertions(+), 1 deletion(-) diff --git a/build/version.props b/build/version.props index f3aa112d..67aaf028 100644 --- a/build/version.props +++ b/build/version.props @@ -5,7 +5,7 @@ 0.4.1 0.4.1 0.4.1 - 0.3.0 + 0.4.1 0.3.2 0.3.2 0.3.0 diff --git a/src/EasyCaching.Core/IEasyCachingProvider.cs b/src/EasyCaching.Core/IEasyCachingProvider.cs index 677ee93b..be3b5940 100644 --- a/src/EasyCaching.Core/IEasyCachingProvider.cs +++ b/src/EasyCaching.Core/IEasyCachingProvider.cs @@ -237,5 +237,25 @@ public interface IEasyCachingProvider /// /// The get stats. CacheStats CacheStats { get; } + + /// + /// Tries the set. + /// + /// true, if set was tryed, false otherwise. + /// Cache key. + /// Cache value. + /// Expiration. + /// The 1st type parameter. + bool TrySet(string cacheKey, T cacheValue, TimeSpan expiration); + + /// + /// Tries the set async. + /// + /// The set async. + /// Cache key. + /// Cache value. + /// Expiration. + /// The 1st type parameter. + Task TrySetAsync(string cacheKey, T cacheValue, TimeSpan expiration); } } diff --git a/src/EasyCaching.HybridCache/HybridCachingProvider.cs b/src/EasyCaching.HybridCache/HybridCachingProvider.cs index 7c208b24..84e59a22 100644 --- a/src/EasyCaching.HybridCache/HybridCachingProvider.cs +++ b/src/EasyCaching.HybridCache/HybridCachingProvider.cs @@ -658,5 +658,15 @@ public async Task FlushAsync() await Task.WhenAll(tasks); } + + public bool TrySet(string cacheKey, T cacheValue, TimeSpan expiration) + { + throw new NotImplementedException(); + } + + public Task TrySetAsync(string cacheKey, T cacheValue, TimeSpan expiration) + { + throw new NotImplementedException(); + } } } diff --git a/src/EasyCaching.InMemory/DefaultInMemoryCachingProvider.cs b/src/EasyCaching.InMemory/DefaultInMemoryCachingProvider.cs index 4078b68f..d519d156 100644 --- a/src/EasyCaching.InMemory/DefaultInMemoryCachingProvider.cs +++ b/src/EasyCaching.InMemory/DefaultInMemoryCachingProvider.cs @@ -710,5 +710,63 @@ private string BuildCacheKey(string prividerName, string cacheKey) ? cacheKey : $"{prividerName}-{cacheKey}"; } + + /// + /// Tries the set. + /// + /// true, if set was tryed, false otherwise. + /// Cache key. + /// Cache value. + /// Expiration. + /// The 1st type parameter. + public bool TrySet(string cacheKey, T cacheValue, TimeSpan expiration) + { + ArgumentCheck.NotNullOrWhiteSpace(cacheKey, nameof(cacheKey)); + ArgumentCheck.NotNull(cacheValue, nameof(cacheValue)); + ArgumentCheck.NotNegativeOrZero(expiration, nameof(expiration)); + + if (_cacheKeys.Contains(cacheKey)) + { + return false; + } + + if (MaxRdSecond > 0) + { + var addSec = new Random().Next(1, MaxRdSecond); + expiration.Add(new TimeSpan(0, 0, addSec)); + } + + Set(cacheKey, cacheValue, expiration); + return true; + } + + /// + /// Tries the set async. + /// + /// The set async. + /// Cache key. + /// Cache value. + /// Expiration. + /// The 1st type parameter. + public async Task TrySetAsync(string cacheKey, T cacheValue, TimeSpan expiration) + { + ArgumentCheck.NotNullOrWhiteSpace(cacheKey, nameof(cacheKey)); + ArgumentCheck.NotNull(cacheValue, nameof(cacheValue)); + ArgumentCheck.NotNegativeOrZero(expiration, nameof(expiration)); + + if (_cacheKeys.Contains(cacheKey)) + { + return false; + } + + if (MaxRdSecond > 0) + { + var addSec = new Random().Next(1, MaxRdSecond); + expiration.Add(new TimeSpan(0, 0, addSec)); + } + + await SetAsync(cacheKey, cacheValue, expiration); + return true; + } } } diff --git a/src/EasyCaching.Memcached/DefaultMemcachedCachingProvider.cs b/src/EasyCaching.Memcached/DefaultMemcachedCachingProvider.cs index b7c27bb9..9e14ed4f 100644 --- a/src/EasyCaching.Memcached/DefaultMemcachedCachingProvider.cs +++ b/src/EasyCaching.Memcached/DefaultMemcachedCachingProvider.cs @@ -641,5 +641,51 @@ public async Task FlushAsync() await _memcachedClient.FlushAllAsync(); } + /// + /// Tries the set. + /// + /// true, if set was tryed, false otherwise. + /// Cache key. + /// Cache value. + /// Expiration. + /// The 1st type parameter. + public bool TrySet(string cacheKey, T cacheValue, TimeSpan expiration) + { + ArgumentCheck.NotNullOrWhiteSpace(cacheKey, nameof(cacheKey)); + ArgumentCheck.NotNull(cacheValue, nameof(cacheValue)); + ArgumentCheck.NotNegativeOrZero(expiration, nameof(expiration)); + + if (MaxRdSecond > 0) + { + var addSec = new Random().Next(1, MaxRdSecond); + expiration.Add(new TimeSpan(0, 0, addSec)); + } + + return _memcachedClient.Store(Enyim.Caching.Memcached.StoreMode.Add, this.HandleCacheKey(cacheKey), cacheValue, expiration); + } + + /// + /// Tries the set async. + /// + /// The set async. + /// Cache key. + /// Cache value. + /// Expiration. + /// The 1st type parameter. + public Task TrySetAsync(string cacheKey, T cacheValue, TimeSpan expiration) + { + ArgumentCheck.NotNullOrWhiteSpace(cacheKey, nameof(cacheKey)); + ArgumentCheck.NotNull(cacheValue, nameof(cacheValue)); + ArgumentCheck.NotNegativeOrZero(expiration, nameof(expiration)); + + if (MaxRdSecond > 0) + { + var addSec = new Random().Next(1, MaxRdSecond); + expiration.Add(new TimeSpan(0, 0, addSec)); + } + + return _memcachedClient.StoreAsync(Enyim.Caching.Memcached.StoreMode.Add, this.HandleCacheKey(cacheKey), cacheValue, expiration); + } + } } diff --git a/src/EasyCaching.Redis/DefaultRedisCachingProvider.cs b/src/EasyCaching.Redis/DefaultRedisCachingProvider.cs index d3e09ffe..f3c0553e 100644 --- a/src/EasyCaching.Redis/DefaultRedisCachingProvider.cs +++ b/src/EasyCaching.Redis/DefaultRedisCachingProvider.cs @@ -752,5 +752,61 @@ public async Task FlushAsync() await Task.WhenAll(tasks); } + + /// + /// Tries the set. + /// + /// true, if set was tryed, false otherwise. + /// Cache key. + /// Cache value. + /// Expiration. + /// The 1st type parameter. + public bool TrySet(string cacheKey, T cacheValue, TimeSpan expiration) + { + ArgumentCheck.NotNullOrWhiteSpace(cacheKey, nameof(cacheKey)); + ArgumentCheck.NotNull(cacheValue, nameof(cacheValue)); + ArgumentCheck.NotNegativeOrZero(expiration, nameof(expiration)); + + if (MaxRdSecond > 0) + { + var addSec = new Random().Next(1, MaxRdSecond); + expiration.Add(new TimeSpan(0, 0, addSec)); + } + + return _cache.StringSet( + cacheKey, + _serializer.Serialize(cacheValue), + expiration, + When.NotExists + ); + } + + /// + /// Tries the set async. + /// + /// The set async. + /// Cache key. + /// Cache value. + /// Expiration. + /// The 1st type parameter. + public Task TrySetAsync(string cacheKey, T cacheValue, TimeSpan expiration) + { + ArgumentCheck.NotNullOrWhiteSpace(cacheKey, nameof(cacheKey)); + ArgumentCheck.NotNull(cacheValue, nameof(cacheValue)); + ArgumentCheck.NotNegativeOrZero(expiration, nameof(expiration)); + + if (MaxRdSecond > 0) + { + var addSec = new Random().Next(1, MaxRdSecond); + expiration.Add(new TimeSpan(0, 0, addSec)); + } + + return _cache.StringSetAsync( + cacheKey, + _serializer.Serialize(cacheValue), + expiration, + When.NotExists + ); + } } } diff --git a/src/EasyCaching.SQLite/ConstSQL.cs b/src/EasyCaching.SQLite/ConstSQL.cs index e58b9013..5d230930 100644 --- a/src/EasyCaching.SQLite/ConstSQL.cs +++ b/src/EasyCaching.SQLite/ConstSQL.cs @@ -21,6 +21,20 @@ INSERT INTO [easycaching] ,@cachevalue ,(select strftime('%s','now')) + @expiration);"; + /// + /// The trysetsql. + /// + public const string TRYSETSQL = @" + INSERT INTO [easycaching] + ([name] + ,[cachekey] + ,[cachevalue] + ,[expiration]) + SELECT @name,@cachekey,@cachevalue,(select strftime('%s','now') + @expiration) + WHERE NOT EXISTS (SELECT 1 FROM [easycaching] WHERE [cachekey] = @cachekey AND [name]=@name AND [expiration] > strftime('%s','now'));"; + + + /// /// The getsql. /// diff --git a/src/EasyCaching.SQLite/DefaultSQLiteCachingProvider.cs b/src/EasyCaching.SQLite/DefaultSQLiteCachingProvider.cs index 8fe9841f..27ac85e9 100644 --- a/src/EasyCaching.SQLite/DefaultSQLiteCachingProvider.cs +++ b/src/EasyCaching.SQLite/DefaultSQLiteCachingProvider.cs @@ -678,5 +678,67 @@ public int GetCount(string prefix = "") /// /// The async. public async Task FlushAsync() => await _cache.ExecuteAsync(ConstSQL.FLUSHSQL,new { name = _name }); + + /// + /// Tries the set. + /// + /// true, if set was tryed, false otherwise. + /// Cache key. + /// Cache value. + /// Expiration. + /// The 1st type parameter. + public bool TrySet(string cacheKey, T cacheValue, TimeSpan expiration) + { + ArgumentCheck.NotNullOrWhiteSpace(cacheKey, nameof(cacheKey)); + ArgumentCheck.NotNull(cacheValue, nameof(cacheValue)); + ArgumentCheck.NotNegativeOrZero(expiration, nameof(expiration)); + + if (MaxRdSecond > 0) + { + var addSec = new Random().Next(1, MaxRdSecond); + expiration.Add(new TimeSpan(0, 0, addSec)); + } + + var rows = _cache.Execute(ConstSQL.TRYSETSQL, new + { + cachekey = cacheKey, + name = _name, + cachevalue = Newtonsoft.Json.JsonConvert.SerializeObject(cacheValue), + expiration = expiration.Ticks / 10000000 + }); + + return rows > 0; + } + + /// + /// Tries the set async. + /// + /// The set async. + /// Cache key. + /// Cache value. + /// Expiration. + /// The 1st type parameter. + public async Task TrySetAsync(string cacheKey, T cacheValue, TimeSpan expiration) + { + ArgumentCheck.NotNullOrWhiteSpace(cacheKey, nameof(cacheKey)); + ArgumentCheck.NotNull(cacheValue, nameof(cacheValue)); + ArgumentCheck.NotNegativeOrZero(expiration, nameof(expiration)); + + if (MaxRdSecond > 0) + { + var addSec = new Random().Next(1, MaxRdSecond); + expiration.Add(new TimeSpan(0, 0, addSec)); + } + + var rows = await _cache.ExecuteAsync(ConstSQL.TRYSETSQL, new + { + cachekey = cacheKey, + name = _name, + cachevalue = Newtonsoft.Json.JsonConvert.SerializeObject(cacheValue), + expiration = expiration.Ticks / 10000000 + }); + + return rows > 0; + } } } diff --git a/test/EasyCaching.UnitTests/CachingTests/BaseCachingProviderTest.cs b/test/EasyCaching.UnitTests/CachingTests/BaseCachingProviderTest.cs index e1249f16..59dfd0b0 100644 --- a/test/EasyCaching.UnitTests/CachingTests/BaseCachingProviderTest.cs +++ b/test/EasyCaching.UnitTests/CachingTests/BaseCachingProviderTest.cs @@ -1036,6 +1036,43 @@ protected virtual void Get_Count_With_Prefix_Should_Succeed() } #endregion + #region TrySet + [Fact] + protected virtual void TrySet_Value_And_Get_Cached_Value_Should_Succeed() + { + var cacheKey = Guid.NewGuid().ToString(); + var cacheValue1 = "value1"; + var cacheValue2 = "value2"; + + var first = _provider.TrySet(cacheKey, cacheValue1, _defaultTs); + var second = _provider.TrySet(cacheKey, cacheValue2, _defaultTs); + + Assert.True(first); + Assert.False(second); + + var val = _provider.Get(cacheKey); + Assert.True(val.HasValue); + Assert.Equal(cacheValue1, val.Value); + } + + [Fact] + protected virtual async Task TrySet_Value_And_Get_Cached_Value_Async_Should_Succeed() + { + var cacheKey = Guid.NewGuid().ToString(); + var cacheValue1 = "value1"; + var cacheValue2 = "value2"; + + var first = await _provider.TrySetAsync(cacheKey, cacheValue1, _defaultTs); + var second = await _provider.TrySetAsync(cacheKey, cacheValue2, _defaultTs); + + Assert.True(first); + Assert.False(second); + + var val = _provider.Get(cacheKey); + Assert.True(val.HasValue); + Assert.Equal(cacheValue1, val.Value); + } + #endregion #region common method protected Dictionary GetMultiDict(string prefix = "") diff --git a/test/EasyCaching.UnitTests/CachingTests/HybridCachingTest.cs b/test/EasyCaching.UnitTests/CachingTests/HybridCachingTest.cs index 6e5f9998..e8681520 100644 --- a/test/EasyCaching.UnitTests/CachingTests/HybridCachingTest.cs +++ b/test/EasyCaching.UnitTests/CachingTests/HybridCachingTest.cs @@ -10,6 +10,7 @@ using Microsoft.Extensions.Options; using System; using System.Collections.Generic; + using System.Threading.Tasks; using Xunit; public class HybridCachingTest : BaseCachingProviderTest @@ -62,5 +63,17 @@ protected override void OnHit_Should_Return_Zero_And_OnMiss_Should_Return_One() { } + + [Fact] + protected override void TrySet_Value_And_Get_Cached_Value_Should_Succeed() + { + + } + + [Fact] + protected override async Task TrySet_Value_And_Get_Cached_Value_Async_Should_Succeed() + { + await Task.FromResult(1); + } } } From 85290ea7f05259378a8103e2095672dfd8442ca1 Mon Sep 17 00:00:00 2001 From: catcherwong Date: Sat, 5 Jan 2019 19:44:30 +0800 Subject: [PATCH 2/3] :bookmark: Release a new version. --- build/releasenotes.props | 13 ++++++------- build/version.props | 12 ++++++------ 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/build/releasenotes.props b/build/releasenotes.props index 7c7f9107..e5e2808b 100644 --- a/build/releasenotes.props +++ b/build/releasenotes.props @@ -1,23 +1,22 @@ - 1. Make IEasyCaching Obsolete. - 2. Remove the class restrict + 1. TrySet/TrySetAsync. - 1. Remove the class restrict + 1. TrySet/TrySetAsync. - 1. Remove the class restrict + 1. TrySet/TrySetAsync. - 1. Remove the class restrict + 1. TrySet/TrySetAsync. - 1. Remove the class restrict + 1. TrySet/TrySetAsync. - 1. Remove the class restrict + 1. TrySet/TrySetAsync. 1. Remove Dependency of IEasyCaching. diff --git a/build/version.props b/build/version.props index 67aaf028..ad478b11 100644 --- a/build/version.props +++ b/build/version.props @@ -1,11 +1,11 @@ - 0.4.1 - 0.4.1 - 0.4.1 - 0.4.1 - 0.4.1 - 0.4.1 + 0.4.2 + 0.4.2 + 0.4.2 + 0.4.2 + 0.4.2 + 0.4.2 0.3.2 0.3.2 0.3.0 From 38697cee4bdaaee1a727e375b8610a4e92d229de Mon Sep 17 00:00:00 2001 From: catcherwong Date: Sat, 5 Jan 2019 20:03:08 +0800 Subject: [PATCH 3/3] :bug: Fix bug of InMemory provider. --- .../DefaultInMemoryCachingProvider.cs | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/src/EasyCaching.InMemory/DefaultInMemoryCachingProvider.cs b/src/EasyCaching.InMemory/DefaultInMemoryCachingProvider.cs index d519d156..30d9a679 100644 --- a/src/EasyCaching.InMemory/DefaultInMemoryCachingProvider.cs +++ b/src/EasyCaching.InMemory/DefaultInMemoryCachingProvider.cs @@ -725,17 +725,11 @@ public bool TrySet(string cacheKey, T cacheValue, TimeSpan expiration) ArgumentCheck.NotNull(cacheValue, nameof(cacheValue)); ArgumentCheck.NotNegativeOrZero(expiration, nameof(expiration)); - if (_cacheKeys.Contains(cacheKey)) + if (_cacheKeys.Contains(BuildCacheKey(Name, cacheKey))) { return false; } - if (MaxRdSecond > 0) - { - var addSec = new Random().Next(1, MaxRdSecond); - expiration.Add(new TimeSpan(0, 0, addSec)); - } - Set(cacheKey, cacheValue, expiration); return true; } @@ -754,17 +748,11 @@ public async Task TrySetAsync(string cacheKey, T cacheValue, TimeSpan e ArgumentCheck.NotNull(cacheValue, nameof(cacheValue)); ArgumentCheck.NotNegativeOrZero(expiration, nameof(expiration)); - if (_cacheKeys.Contains(cacheKey)) + if (_cacheKeys.Contains(BuildCacheKey(Name, cacheKey))) { return false; } - if (MaxRdSecond > 0) - { - var addSec = new Random().Next(1, MaxRdSecond); - expiration.Add(new TimeSpan(0, 0, addSec)); - } - await SetAsync(cacheKey, cacheValue, expiration); return true; }