diff --git a/Enyim.Caching.Tests/MemcachedClientCasTests.cs b/Enyim.Caching.Tests/MemcachedClientCasTests.cs index d80317ab..cca07dd6 100644 --- a/Enyim.Caching.Tests/MemcachedClientCasTests.cs +++ b/Enyim.Caching.Tests/MemcachedClientCasTests.cs @@ -1,14 +1,13 @@ using Enyim.Caching.Memcached; +using Newtonsoft.Json.Linq; using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; +using System.Threading.Tasks; using Xunit; namespace Enyim.Caching.Tests { - public class MemcachedClientCasTests : MemcachedClientTestsBase + public class MemcachedClientCasTests : MemcachedClientTestsBase { [Fact] @@ -35,7 +34,72 @@ public void When_Storing_Item_With_Invalid_Cas_Result_Is_Not_Successful() StoreAssertFail(casResult); } - } + + [Fact] + public async Task When_Storing_Item_With_Valid_Cas_Result_Is_Successful_Async() + { + var key = GetUniqueKey("cas"); + var value = GetRandomString(); + var comment = new Comment + { + Author = key, + Text = value + }; + + var storeResult = Store(StoreMode.Add, key, comment); + StoreAssertPass(storeResult); + + var casResult1 = await _client.GetAsync(key); + GetAssertPass(casResult1, comment); + + var casResult2 = await _client.GetAsync(key); + GetAssertPass(casResult2, comment); + } + + /// + /// comment + /// because use as default serialization tool, + /// so will return with as 's type. + /// + public class Comment : IEquatable, IEquatable + { + public string Author { get; set; } + + public string Text { get; set; } + + public bool Equals(Comment other) + { + return other != null && other.Author == Author && other.Text == Text; + } + + public bool Equals(JObject other) + { + return Equals(other?.ToObject()); + } + + public override bool Equals(object obj) + { + if (obj == null) + { + return false; + } + if (obj is Comment comment) + { + return Equals(comment); + } + if (obj is JObject jObject) + { + return Equals(jObject); + } + return false; + } + + public override int GetHashCode() + { + return HashCode.Combine(Author, Text); + } + } + } } #region [ License information ] diff --git a/Enyim.Caching.Tests/MemcachedClientMutateTests.cs b/Enyim.Caching.Tests/MemcachedClientMutateTests.cs index 07564789..dd93f1b5 100644 --- a/Enyim.Caching.Tests/MemcachedClientMutateTests.cs +++ b/Enyim.Caching.Tests/MemcachedClientMutateTests.cs @@ -36,12 +36,16 @@ public async Task When_Touch_Item_Result_Is_Successful() { var key = GetUniqueKey("touch"); await _client.AddAsync(key, "value", 1); - Assert.True((await _client.GetAsync(key)).Success); + var operationResult = await _client.GetAsync(key); + Assert.True(operationResult.Success); + Assert.NotEqual(0UL, operationResult.Cas); var result = await _client.TouchAsync(key, TimeSpan.FromSeconds(60)); await Task.Delay(1010); Assert.True(result.Success, "Success was false"); Assert.True((result.StatusCode ?? 0) == 0, "StatusCode was not null or 0"); - Assert.True((await _client.GetAsync(key)).Success); + operationResult = await _client.GetAsync(key); + Assert.True(operationResult.Success); + Assert.NotEqual(0UL, operationResult.Cas); } } } diff --git a/Enyim.Caching.Tests/MemcachedClientTestsBase.cs b/Enyim.Caching.Tests/MemcachedClientTestsBase.cs index d1ae4862..a9048b96 100644 --- a/Enyim.Caching.Tests/MemcachedClientTestsBase.cs +++ b/Enyim.Caching.Tests/MemcachedClientTestsBase.cs @@ -105,6 +105,14 @@ protected void GetAssertPass(IGetOperationResult result, object expectedValue) Assert.Equal(expectedValue, result.Value); } + protected void GetAssertPass(IGetOperationResult result, T expectedValue) + { + Assert.True(result.Success, "Success was false"); + Assert.True(result.Cas > 0, "Cas value was 0"); + Assert.True((result.StatusCode ?? 0) == 0, "StatusCode was neither 0 nor null"); + Assert.Equal(expectedValue, result.Value); + } + protected void GetAssertFail(IGetOperationResult result) { Assert.False(result.Success, "Success was true"); diff --git a/Enyim.Caching/IMemcachedClient.cs b/Enyim.Caching/IMemcachedClient.cs index 7adf8fa1..fb6b0ac4 100644 --- a/Enyim.Caching/IMemcachedClient.cs +++ b/Enyim.Caching/IMemcachedClient.cs @@ -17,6 +17,7 @@ public interface IMemcachedClient : IDisposable bool Replace(string key, object value, int cacheSeconds); Task ReplaceAsync(string key, object value, int cacheSeconds); + Task GetAsync(string key); Task> GetAsync(string key); Task GetValueAsync(string key); Task GetValueOrCreateAsync(string key, int cacheSeconds, Func> generator); diff --git a/Enyim.Caching/MemcachedClient.cs b/Enyim.Caching/MemcachedClient.cs index 197b0eea..3447b644 100755 --- a/Enyim.Caching/MemcachedClient.cs +++ b/Enyim.Caching/MemcachedClient.cs @@ -160,6 +160,25 @@ public IGetOperationResult PerformGet(string key) } } + private bool CreateGetCommand(string key, out IGetOperationResult result, out IMemcachedNode node, out IGetOperation command) + { + result = new DefaultGetOperationResultFactory().Create(); + var hashedKey = this.keyTransformer.Transform(key); + + node = this.pool.Locate(hashedKey); + if (node == null) + { + var errorMessage = $"Unable to locate node with \"{key}\" key"; + _logger.LogError(errorMessage); + result.Fail(errorMessage); + command = null; + return false; + } + + command = this.pool.OperationFactory.Get(hashedKey); + return true; + } + private bool CreateGetCommand(string key, out IGetOperationResult result, out IMemcachedNode node, out IGetOperation command) { result = new DefaultGetOperationResultFactory().Create(); @@ -179,11 +198,28 @@ private bool CreateGetCommand(string key, out IGetOperationResult result, return true; } + private IGetOperationResult BuildGetCommandResult(IGetOperationResult result, IGetOperation command, IOperationResult commandResult) + { + if (commandResult.Success) + { + result.Value = transcoder.Deserialize(command.Result); + result.Cas = command.CasValue; + result.Pass(); + } + else + { + commandResult.Combine(result); + } + + return result; + } + private IGetOperationResult BuildGetCommandResult(IGetOperationResult result, IGetOperation command, IOperationResult commandResult) { if (commandResult.Success) { result.Value = transcoder.Deserialize(command.Result); + result.Cas = command.CasValue; result.Pass(); } else @@ -194,6 +230,27 @@ private IGetOperationResult BuildGetCommandResult(IGetOperationResult r return result; } + public async Task GetAsync(string key) + { + if (!CreateGetCommand(key, out var result, out var node, out var command)) + { + _logger.LogInformation($"Failed to CreateGetCommand for '{key}' key"); + return result; + } + + try + { + var commandResult = await node.ExecuteAsync(command); + return BuildGetCommandResult(result, command, commandResult); + } + catch (Exception ex) + { + _logger.LogError(0, ex, $"{nameof(GetAsync)}(\"{key}\")"); + result.Fail(ex.Message, ex); + return result; + } + } + public async Task> GetAsync(string key) { if (!CreateGetCommand(key, out var result, out var node, out var command)) diff --git a/Enyim.Caching/MemcachedClientT.cs b/Enyim.Caching/MemcachedClientT.cs index 183561a3..8e6cab44 100644 --- a/Enyim.Caching/MemcachedClientT.cs +++ b/Enyim.Caching/MemcachedClientT.cs @@ -120,6 +120,11 @@ public IDictionary Get(IEnumerable keys) return _memcachedClient.Get(keys); } + public Task GetAsync(string key) + { + return _memcachedClient.GetAsync(key); + } + public Task> GetAsync(string key) { return _memcachedClient.GetAsync(key); diff --git a/Enyim.Caching/NullMemcachedClient.cs b/Enyim.Caching/NullMemcachedClient.cs index 5056bc23..b56d1946 100644 --- a/Enyim.Caching/NullMemcachedClient.cs +++ b/Enyim.Caching/NullMemcachedClient.cs @@ -1,8 +1,6 @@ using System; using System.Collections.Generic; -using System.Linq; using System.Net; -using System.Text; using System.Threading.Tasks; using Enyim.Caching.Memcached; using Enyim.Caching.Memcached.Results; @@ -104,6 +102,13 @@ public T Get(string key) return default(T); } + public Task GetAsync(string key) + { + var result = new DefaultGetOperationResultFactory().Create(); + result.Success = false; + return Task.FromResult(result); + } + public async Task> GetAsync(string key) { var result = new DefaultGetOperationResultFactory().Create();