From 135bd51500b22190249f739df0076b202cb7390a Mon Sep 17 00:00:00 2001 From: "yaorong.liang" Date: Fri, 22 Feb 2019 11:56:21 +0800 Subject: [PATCH 1/4] :bug: Fixed some bugs of Interceptor.AspectCore. --- .../Controllers/ValuesController.cs | 10 ++++++++++ .../Services/IAspectCoreService.cs | 14 ++++++++++++++ .../Services/ICastleService.cs | 8 ++++++++ .../EasyCachingInterceptor.cs | 7 ++++--- 4 files changed, 36 insertions(+), 3 deletions(-) diff --git a/sample/EasyCaching.Demo.Interceptors/Controllers/ValuesController.cs b/sample/EasyCaching.Demo.Interceptors/Controllers/ValuesController.cs index 9d0abdf3..a7591e19 100644 --- a/sample/EasyCaching.Demo.Interceptors/Controllers/ValuesController.cs +++ b/sample/EasyCaching.Demo.Interceptors/Controllers/ValuesController.cs @@ -57,6 +57,11 @@ public async Task AspectcoreAsync(int type = 1) var res = await _aService.GetDemoAsync(999); return $"{res.Id}-{res.Name}-{res.CreateTime}"; } + else if (type == 3) + { + var res = await _aService.GetDemoListAsync(999); + return $"{res.Count}"; + } else { return await Task.FromResult("wait"); @@ -104,6 +109,11 @@ public async Task CastleAsync(int type = 1) var res = await _cService.GetDemoAsync(999); return $"{res.Id}-{res.Name}-{res.CreateTime}"; } + else if (type == 3) + { + var res = await _aService.GetDemoListAsync(999); + return $"{res.Count}"; + } else { return await Task.FromResult("wait"); diff --git a/sample/EasyCaching.Demo.Interceptors/Services/IAspectCoreService.cs b/sample/EasyCaching.Demo.Interceptors/Services/IAspectCoreService.cs index 0625e94e..2411b7e8 100644 --- a/sample/EasyCaching.Demo.Interceptors/Services/IAspectCoreService.cs +++ b/sample/EasyCaching.Demo.Interceptors/Services/IAspectCoreService.cs @@ -21,6 +21,10 @@ public interface IAspectCoreService //: EasyCaching.Core.Internal.IEasyCaching [EasyCachingAble(Expiration = 10)] Task GetDemoAsync(int id); + [EasyCachingAble(Expiration = 10)] + Task> GetDemoListAsync(int id); + + [EasyCachingAble(Expiration = 10)] Demo GetDemo(int id); } @@ -47,11 +51,21 @@ public Task GetDemoAsync(int id) return Task.FromResult(new Demo { Id = id, CreateTime = System.DateTime.Now, Name = "catcher" }); } + public Task> GetDemoListAsync(int id) + { + return Task.FromResult(new System.Collections.Generic.List() { new Demo { Id = id, CreateTime = System.DateTime.Now, Name = "catcher" } }); + } + public async Task GetUtcTimeAsync() { return await Task.FromResult(System.DateTimeOffset.UtcNow.ToString()); } + public async Task DeleteSomethingAsync(int id) + { + await Task.Run(() => System.Console.WriteLine("Handle delete something..")); + } + public string PutSomething(string str) { return str; diff --git a/sample/EasyCaching.Demo.Interceptors/Services/ICastleService.cs b/sample/EasyCaching.Demo.Interceptors/Services/ICastleService.cs index 2bfc560d..593c9a6b 100644 --- a/sample/EasyCaching.Demo.Interceptors/Services/ICastleService.cs +++ b/sample/EasyCaching.Demo.Interceptors/Services/ICastleService.cs @@ -21,6 +21,9 @@ public interface ICastleService [EasyCachingAble(Expiration = 10)] Task GetDemoAsync(int id); + [EasyCachingAble(Expiration = 10)] + Task> GetDemoListAsync(int id); + [EasyCachingAble(Expiration = 10)] Demo GetDemo(int id); } @@ -47,6 +50,11 @@ public Task GetDemoAsync(int id) return Task.FromResult(new Demo { Id = id, CreateTime = System.DateTime.Now, Name = "catcher" }); } + public Task> GetDemoListAsync(int id) + { + return Task.FromResult(new System.Collections.Generic.List() { new Demo { Id = id, CreateTime = System.DateTime.Now, Name = "catcher" } }); + } + public async Task GetUtcTimeAsync() { return await Task.FromResult(System.DateTimeOffset.UtcNow.ToString()); diff --git a/src/EasyCaching.Interceptor.AspectCore/EasyCachingInterceptor.cs b/src/EasyCaching.Interceptor.AspectCore/EasyCachingInterceptor.cs index 691add02..0f227cb2 100644 --- a/src/EasyCaching.Interceptor.AspectCore/EasyCachingInterceptor.cs +++ b/src/EasyCaching.Interceptor.AspectCore/EasyCachingInterceptor.cs @@ -66,13 +66,14 @@ private async Task ProceedAbleAsync(AspectContext context, AspectDelegate next) { if (context.ServiceMethod.GetCustomAttributes(true).FirstOrDefault(x => x.GetType() == typeof(EasyCachingAbleAttribute)) is EasyCachingAbleAttribute attribute) { - var cacheKey = KeyGenerator.GetCacheKey(context.ServiceMethod, context.Parameters, attribute.CacheKeyPrefix); - var cacheValue = await CacheProvider.GetAsync(cacheKey, context.ServiceMethod.ReturnType); - var returnType = context.IsAsync() ? context.ServiceMethod.ReturnType.GetGenericArguments().First() : context.ServiceMethod.ReturnType; + var cacheKey = KeyGenerator.GetCacheKey(context.ServiceMethod, context.Parameters, attribute.CacheKeyPrefix); + + object cacheValue = await CacheProvider.GetAsync(cacheKey, returnType); + if (cacheValue != null) { if (context.IsAsync()) From e20fc0e3e652f1f1a42806cc7013056cf2619f09 Mon Sep 17 00:00:00 2001 From: "yaorong.liang" Date: Fri, 22 Feb 2019 11:56:36 +0800 Subject: [PATCH 2/4] :bug: Fixed some bugs of Interceptor.Castle. --- .../EasyCaching.Interceptor.Castle.csproj | 1 + .../EasyCachingInterceptor.cs | 53 ++++++++++- .../ReflectionExtensions.cs | 95 +++++++++++++++++++ .../TypeExtensions.cs | 31 ++++++ 4 files changed, 175 insertions(+), 5 deletions(-) create mode 100644 src/EasyCaching.Interceptor.Castle/ReflectionExtensions.cs create mode 100644 src/EasyCaching.Interceptor.Castle/TypeExtensions.cs diff --git a/src/EasyCaching.Interceptor.Castle/EasyCaching.Interceptor.Castle.csproj b/src/EasyCaching.Interceptor.Castle/EasyCaching.Interceptor.Castle.csproj index 3b1b0d85..ae0990d6 100644 --- a/src/EasyCaching.Interceptor.Castle/EasyCaching.Interceptor.Castle.csproj +++ b/src/EasyCaching.Interceptor.Castle/EasyCaching.Interceptor.Castle.csproj @@ -28,5 +28,6 @@ + diff --git a/src/EasyCaching.Interceptor.Castle/EasyCachingInterceptor.cs b/src/EasyCaching.Interceptor.Castle/EasyCachingInterceptor.cs index 23f9f851..348ec159 100644 --- a/src/EasyCaching.Interceptor.Castle/EasyCachingInterceptor.cs +++ b/src/EasyCaching.Interceptor.Castle/EasyCachingInterceptor.cs @@ -1,7 +1,9 @@ namespace EasyCaching.Interceptor.Castle { using System; + using System.Collections.Concurrent; using System.Linq; + using System.Reflection; using System.Threading.Tasks; using EasyCaching.Core; using EasyCaching.Core.Interceptor; @@ -22,6 +24,12 @@ public class EasyCachingInterceptor : IInterceptor /// private readonly IEasyCachingKeyGenerator _keyGenerator; + /// + /// The typeof task result method. + /// + private static readonly ConcurrentDictionary + TypeofTaskResultMethod = new ConcurrentDictionary(); + /// /// Initializes a new instance of the class. /// @@ -63,14 +71,26 @@ private void ProceedAble(IInvocation invocation) if (serviceMethod.GetCustomAttributes(true).FirstOrDefault(x => x.GetType() == typeof(EasyCachingAbleAttribute)) is EasyCachingAbleAttribute attribute) { - var cacheKey = _keyGenerator.GetCacheKey(serviceMethod, invocation.Arguments, attribute.CacheKeyPrefix); + var returnType = serviceMethod.IsReturnTask() + ? serviceMethod.ReturnType.GetGenericArguments().First() + : serviceMethod.ReturnType; - var cacheValue = (_cacheProvider.GetAsync(cacheKey, serviceMethod.ReturnType)).GetAwaiter().GetResult(); + var cacheKey = _keyGenerator.GetCacheKey(serviceMethod, invocation.Arguments, attribute.CacheKeyPrefix); + + var cacheValue = (_cacheProvider.GetAsync(cacheKey, returnType)).GetAwaiter().GetResult(); if (cacheValue != null) { - invocation.ReturnValue = cacheValue; + if (serviceMethod.IsReturnTask()) + { + invocation.ReturnValue = + TypeofTaskResultMethod.GetOrAdd(returnType, t => typeof(Task).GetMethods().First(p => p.Name == "FromResult" && p.ContainsGenericParameters).MakeGenericMethod(returnType)).Invoke(null, new object[] { cacheValue }); + } + else + { + invocation.ReturnValue = cacheValue; + } } else { @@ -78,7 +98,20 @@ private void ProceedAble(IInvocation invocation) invocation.Proceed(); if (!string.IsNullOrWhiteSpace(cacheKey) && invocation.ReturnValue != null) - _cacheProvider.Set(cacheKey, invocation.ReturnValue, TimeSpan.FromSeconds(attribute.Expiration)); + { + if (serviceMethod.IsReturnTask()) + { + //get the result + var returnValue = invocation.UnwrapAsyncReturnValue().Result; + + _cacheProvider.Set(cacheKey, returnValue, TimeSpan.FromSeconds(attribute.Expiration)); + } + else + { + _cacheProvider.Set(cacheKey, invocation.ReturnValue, TimeSpan.FromSeconds(attribute.Expiration)); + } + } + } } else @@ -100,7 +133,17 @@ private void ProcessPut(IInvocation invocation) { var cacheKey = _keyGenerator.GetCacheKey(serviceMethod, invocation.Arguments, attribute.CacheKeyPrefix); - _cacheProvider.Set(cacheKey, invocation.ReturnValue, TimeSpan.FromSeconds(attribute.Expiration)); + if (serviceMethod.IsReturnTask()) + { + //get the result + var returnValue = invocation.UnwrapAsyncReturnValue().Result; + + _cacheProvider.Set(cacheKey, returnValue, TimeSpan.FromSeconds(attribute.Expiration)); + } + else + { + _cacheProvider.Set(cacheKey, invocation.ReturnValue, TimeSpan.FromSeconds(attribute.Expiration)); + } } } diff --git a/src/EasyCaching.Interceptor.Castle/ReflectionExtensions.cs b/src/EasyCaching.Interceptor.Castle/ReflectionExtensions.cs new file mode 100644 index 00000000..dd3180c7 --- /dev/null +++ b/src/EasyCaching.Interceptor.Castle/ReflectionExtensions.cs @@ -0,0 +1,95 @@ +using Castle.DynamicProxy; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; + +namespace EasyCaching.Interceptor.Castle +{ + public static class ReflectionExtensions + { + public static bool IsReturnTask(this MethodInfo methodInfo) + { + if (methodInfo == null) + { + throw new ArgumentNullException(nameof(methodInfo)); + } + var returnType = methodInfo.ReturnType.GetTypeInfo(); + return returnType.IsTaskWithResult(); + } + + public static Task UnwrapAsyncReturnValue(this IInvocation invocation) + { + if (invocation == null) + { + throw new ArgumentNullException(nameof(invocation)); + } + + var serviceMethod = invocation.Method ?? invocation.MethodInvocationTarget; + + if (!serviceMethod.IsReturnTask()) + { + throw new InvalidOperationException("This operation only support asynchronous method."); + } + + var returnValue = invocation.ReturnValue; + if (returnValue == null) + { + return null; + } + + var returnTypeInfo = returnValue.GetType().GetTypeInfo(); + return Unwrap(returnValue, returnTypeInfo); + } + + private static async Task Unwrap(object value, TypeInfo valueTypeInfo) + { + object result = null; + + if (valueTypeInfo.IsTaskWithResult()) + { + // Is there better solution to unwrap ? + result = (object) (await (dynamic) value); + } + else if (value is Task) + { + return null; + } + else + { + result = value; + } + + if (result == null) + { + return null; + } + + var resultTypeInfo = result.GetType().GetTypeInfo(); + if (IsAsyncType(resultTypeInfo)) + { + return Unwrap(result, resultTypeInfo); + } + + return result; + } + + private static bool IsAsyncType(TypeInfo typeInfo) + { + if (typeInfo.IsTask()) + { + return true; + } + + if (typeInfo.IsTaskWithResult()) + { + return true; + } + + + return false; + } + } +} diff --git a/src/EasyCaching.Interceptor.Castle/TypeExtensions.cs b/src/EasyCaching.Interceptor.Castle/TypeExtensions.cs new file mode 100644 index 00000000..e0f183d4 --- /dev/null +++ b/src/EasyCaching.Interceptor.Castle/TypeExtensions.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Concurrent; +using System.Reflection; +using System.Threading.Tasks; + +namespace EasyCaching.Interceptor.Castle +{ + public static class TypeExtensions + { + private static readonly ConcurrentDictionary isTaskOfTCache = new ConcurrentDictionary(); + + public static bool IsTaskWithResult(this TypeInfo typeInfo) + { + if (typeInfo == null) + { + throw new ArgumentNullException(nameof(typeInfo)); + } + return isTaskOfTCache.GetOrAdd(typeInfo, Info => Info.IsGenericType && typeof(Task).GetTypeInfo().IsAssignableFrom(Info)); + } + + public static bool IsTask(this TypeInfo typeInfo) + { + if (typeInfo == null) + { + throw new ArgumentNullException(nameof(typeInfo)); + } + return typeInfo.AsType() == typeof(Task); + } + + } +} From 4e0aa638781d026b389be627fb67048fb6ec36b3 Mon Sep 17 00:00:00 2001 From: "yaorong.liang" Date: Fri, 22 Feb 2019 18:42:35 +0800 Subject: [PATCH 3/4] =?UTF-8?q?=F0=9F=90=9B=20Fixed=20Interceptor.Castle?= =?UTF-8?q?=20Tests.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../InterceptorTests/CastleInterceptorTest.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/EasyCaching.UnitTests/InterceptorTests/CastleInterceptorTest.cs b/test/EasyCaching.UnitTests/InterceptorTests/CastleInterceptorTest.cs index d505070d..0294bfae 100755 --- a/test/EasyCaching.UnitTests/InterceptorTests/CastleInterceptorTest.cs +++ b/test/EasyCaching.UnitTests/InterceptorTests/CastleInterceptorTest.cs @@ -144,10 +144,10 @@ protected virtual async Task Interceptor_Put_With_Task_Method_Should_Succeed() var key = _keyGenerator.GetCacheKey(method, new object[] { 1, "123" }, "CastleExample"); - var value = _cachingProvider.Get>(key); + var value = _cachingProvider.Get(key); Assert.True(value.HasValue); - Assert.Equal(str, value.Value.Result); + Assert.Equal(str, value.Value); } [Fact] From bf31532fc8d8d9147b836461e69a493af6b391cb Mon Sep 17 00:00:00 2001 From: catcherwong Date: Sat, 23 Feb 2019 06:12:43 +0800 Subject: [PATCH 4/4] :bookmark: Release new version for Interceptors --- build/version.props | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build/version.props b/build/version.props index 808bed80..74b409d4 100644 --- a/build/version.props +++ b/build/version.props @@ -6,8 +6,8 @@ 0.5.2 0.5.2 0.5.2 - 0.5.2 - 0.5.2 + 0.5.2.1 + 0.5.2.1 0.5.2 0.5.2 0.5.2