Skip to content

Commit a45b140

Browse files
authored
Merge pull request #84 from dotnetcore/dev
Fixed some bugs of Interceptors
2 parents c8a1e0d + bf31532 commit a45b140

File tree

10 files changed

+215
-12
lines changed

10 files changed

+215
-12
lines changed

build/version.props

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66
<EasyCachingSQLitePackageVersion>0.5.2</EasyCachingSQLitePackageVersion>
77
<EasyCachingInMemoryPackageVersion>0.5.2</EasyCachingInMemoryPackageVersion>
88
<EasyCachingHybridPackageVersion>0.5.2</EasyCachingHybridPackageVersion>
9-
<EasyCachingAspectCorePackageVersion>0.5.2</EasyCachingAspectCorePackageVersion>
10-
<EasyCachingCastlePackageVersion>0.5.2</EasyCachingCastlePackageVersion>
9+
<EasyCachingAspectCorePackageVersion>0.5.2.1</EasyCachingAspectCorePackageVersion>
10+
<EasyCachingCastlePackageVersion>0.5.2.1</EasyCachingCastlePackageVersion>
1111
<EasyCachingResponseCachingPackageVersion>0.5.2</EasyCachingResponseCachingPackageVersion>
1212
<EasyCachingJsonPackageVersion>0.5.2</EasyCachingJsonPackageVersion>
1313
<EasyCachingMessagePackPackageVersion>0.5.2</EasyCachingMessagePackPackageVersion>

sample/EasyCaching.Demo.Interceptors/Controllers/ValuesController.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,11 @@ public async Task<string> AspectcoreAsync(int type = 1)
5757
var res = await _aService.GetDemoAsync(999);
5858
return $"{res.Id}-{res.Name}-{res.CreateTime}";
5959
}
60+
else if (type == 3)
61+
{
62+
var res = await _aService.GetDemoListAsync(999);
63+
return $"{res.Count}";
64+
}
6065
else
6166
{
6267
return await Task.FromResult("wait");
@@ -104,6 +109,11 @@ public async Task<string> CastleAsync(int type = 1)
104109
var res = await _cService.GetDemoAsync(999);
105110
return $"{res.Id}-{res.Name}-{res.CreateTime}";
106111
}
112+
else if (type == 3)
113+
{
114+
var res = await _aService.GetDemoListAsync(999);
115+
return $"{res.Count}";
116+
}
107117
else
108118
{
109119
return await Task.FromResult<string>("wait");

sample/EasyCaching.Demo.Interceptors/Services/IAspectCoreService.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ public interface IAspectCoreService //: EasyCaching.Core.Internal.IEasyCaching
2121
[EasyCachingAble(Expiration = 10)]
2222
Task<Demo> GetDemoAsync(int id);
2323

24+
[EasyCachingAble(Expiration = 10)]
25+
Task<System.Collections.Generic.List<Demo>> GetDemoListAsync(int id);
26+
27+
2428
[EasyCachingAble(Expiration = 10)]
2529
Demo GetDemo(int id);
2630
}
@@ -47,11 +51,21 @@ public Task<Demo> GetDemoAsync(int id)
4751
return Task.FromResult(new Demo { Id = id, CreateTime = System.DateTime.Now, Name = "catcher" });
4852
}
4953

54+
public Task<System.Collections.Generic.List<Demo>> GetDemoListAsync(int id)
55+
{
56+
return Task.FromResult(new System.Collections.Generic.List<Demo>() { new Demo { Id = id, CreateTime = System.DateTime.Now, Name = "catcher" } });
57+
}
58+
5059
public async Task<string> GetUtcTimeAsync()
5160
{
5261
return await Task.FromResult<string>(System.DateTimeOffset.UtcNow.ToString());
5362
}
5463

64+
public async Task DeleteSomethingAsync(int id)
65+
{
66+
await Task.Run(() => System.Console.WriteLine("Handle delete something.."));
67+
}
68+
5569
public string PutSomething(string str)
5670
{
5771
return str;

sample/EasyCaching.Demo.Interceptors/Services/ICastleService.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ public interface ICastleService
2121
[EasyCachingAble(Expiration = 10)]
2222
Task<Demo> GetDemoAsync(int id);
2323

24+
[EasyCachingAble(Expiration = 10)]
25+
Task<System.Collections.Generic.List<Demo>> GetDemoListAsync(int id);
26+
2427
[EasyCachingAble(Expiration = 10)]
2528
Demo GetDemo(int id);
2629
}
@@ -47,6 +50,11 @@ public Task<Demo> GetDemoAsync(int id)
4750
return Task.FromResult(new Demo { Id = id, CreateTime = System.DateTime.Now, Name = "catcher" });
4851
}
4952

53+
public Task<System.Collections.Generic.List<Demo>> GetDemoListAsync(int id)
54+
{
55+
return Task.FromResult(new System.Collections.Generic.List<Demo>() { new Demo { Id = id, CreateTime = System.DateTime.Now, Name = "catcher" } });
56+
}
57+
5058
public async Task<string> GetUtcTimeAsync()
5159
{
5260
return await Task.FromResult<string>(System.DateTimeOffset.UtcNow.ToString());

src/EasyCaching.Interceptor.AspectCore/EasyCachingInterceptor.cs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,13 +66,14 @@ private async Task ProceedAbleAsync(AspectContext context, AspectDelegate next)
6666
{
6767
if (context.ServiceMethod.GetCustomAttributes(true).FirstOrDefault(x => x.GetType() == typeof(EasyCachingAbleAttribute)) is EasyCachingAbleAttribute attribute)
6868
{
69-
var cacheKey = KeyGenerator.GetCacheKey(context.ServiceMethod, context.Parameters, attribute.CacheKeyPrefix);
70-
var cacheValue = await CacheProvider.GetAsync(cacheKey, context.ServiceMethod.ReturnType);
71-
7269
var returnType = context.IsAsync()
7370
? context.ServiceMethod.ReturnType.GetGenericArguments().First()
7471
: context.ServiceMethod.ReturnType;
7572

73+
var cacheKey = KeyGenerator.GetCacheKey(context.ServiceMethod, context.Parameters, attribute.CacheKeyPrefix);
74+
75+
object cacheValue = await CacheProvider.GetAsync(cacheKey, returnType);
76+
7677
if (cacheValue != null)
7778
{
7879
if (context.IsAsync())

src/EasyCaching.Interceptor.Castle/EasyCaching.Interceptor.Castle.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,5 +28,6 @@
2828
<PackageReference Include="Autofac.Extras.DynamicProxy" Version="4.5.0" />
2929
<PackageReference Include="Autofac" Version="4.8.1" />
3030
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="4.3.1" />
31+
<PackageReference Include="Microsoft.CSharp" Version="4.5.0" />
3132
</ItemGroup>
3233
</Project>

src/EasyCaching.Interceptor.Castle/EasyCachingInterceptor.cs

Lines changed: 48 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
namespace EasyCaching.Interceptor.Castle
22
{
33
using System;
4+
using System.Collections.Concurrent;
45
using System.Linq;
6+
using System.Reflection;
57
using System.Threading.Tasks;
68
using EasyCaching.Core;
79
using EasyCaching.Core.Interceptor;
@@ -22,6 +24,12 @@ public class EasyCachingInterceptor : IInterceptor
2224
/// </summary>
2325
private readonly IEasyCachingKeyGenerator _keyGenerator;
2426

27+
/// <summary>
28+
/// The typeof task result method.
29+
/// </summary>
30+
private static readonly ConcurrentDictionary<Type, MethodInfo>
31+
TypeofTaskResultMethod = new ConcurrentDictionary<Type, MethodInfo>();
32+
2533
/// <summary>
2634
/// Initializes a new instance of the <see cref="T:EasyCaching.Interceptor.Castle.EasyCachingInterceptor"/> class.
2735
/// </summary>
@@ -63,22 +71,47 @@ private void ProceedAble(IInvocation invocation)
6371

6472
if (serviceMethod.GetCustomAttributes(true).FirstOrDefault(x => x.GetType() == typeof(EasyCachingAbleAttribute)) is EasyCachingAbleAttribute attribute)
6573
{
66-
var cacheKey = _keyGenerator.GetCacheKey(serviceMethod, invocation.Arguments, attribute.CacheKeyPrefix);
74+
var returnType = serviceMethod.IsReturnTask()
75+
? serviceMethod.ReturnType.GetGenericArguments().First()
76+
: serviceMethod.ReturnType;
6777

68-
var cacheValue = (_cacheProvider.GetAsync(cacheKey, serviceMethod.ReturnType)).GetAwaiter().GetResult();
78+
var cacheKey = _keyGenerator.GetCacheKey(serviceMethod, invocation.Arguments, attribute.CacheKeyPrefix);
79+
80+
var cacheValue = (_cacheProvider.GetAsync(cacheKey, returnType)).GetAwaiter().GetResult();
6981

7082

7183
if (cacheValue != null)
7284
{
73-
invocation.ReturnValue = cacheValue;
85+
if (serviceMethod.IsReturnTask())
86+
{
87+
invocation.ReturnValue =
88+
TypeofTaskResultMethod.GetOrAdd(returnType, t => typeof(Task).GetMethods().First(p => p.Name == "FromResult" && p.ContainsGenericParameters).MakeGenericMethod(returnType)).Invoke(null, new object[] { cacheValue });
89+
}
90+
else
91+
{
92+
invocation.ReturnValue = cacheValue;
93+
}
7494
}
7595
else
7696
{
7797
// Invoke the method if we don't have a cache hit
7898
invocation.Proceed();
7999

80100
if (!string.IsNullOrWhiteSpace(cacheKey) && invocation.ReturnValue != null)
81-
_cacheProvider.Set(cacheKey, invocation.ReturnValue, TimeSpan.FromSeconds(attribute.Expiration));
101+
{
102+
if (serviceMethod.IsReturnTask())
103+
{
104+
//get the result
105+
var returnValue = invocation.UnwrapAsyncReturnValue().Result;
106+
107+
_cacheProvider.Set(cacheKey, returnValue, TimeSpan.FromSeconds(attribute.Expiration));
108+
}
109+
else
110+
{
111+
_cacheProvider.Set(cacheKey, invocation.ReturnValue, TimeSpan.FromSeconds(attribute.Expiration));
112+
}
113+
}
114+
82115
}
83116
}
84117
else
@@ -100,7 +133,17 @@ private void ProcessPut(IInvocation invocation)
100133
{
101134
var cacheKey = _keyGenerator.GetCacheKey(serviceMethod, invocation.Arguments, attribute.CacheKeyPrefix);
102135

103-
_cacheProvider.Set(cacheKey, invocation.ReturnValue, TimeSpan.FromSeconds(attribute.Expiration));
136+
if (serviceMethod.IsReturnTask())
137+
{
138+
//get the result
139+
var returnValue = invocation.UnwrapAsyncReturnValue().Result;
140+
141+
_cacheProvider.Set(cacheKey, returnValue, TimeSpan.FromSeconds(attribute.Expiration));
142+
}
143+
else
144+
{
145+
_cacheProvider.Set(cacheKey, invocation.ReturnValue, TimeSpan.FromSeconds(attribute.Expiration));
146+
}
104147
}
105148
}
106149

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
using Castle.DynamicProxy;
2+
using System;
3+
using System.Collections.Concurrent;
4+
using System.Collections.Generic;
5+
using System.Reflection;
6+
using System.Text;
7+
using System.Threading.Tasks;
8+
9+
namespace EasyCaching.Interceptor.Castle
10+
{
11+
public static class ReflectionExtensions
12+
{
13+
public static bool IsReturnTask(this MethodInfo methodInfo)
14+
{
15+
if (methodInfo == null)
16+
{
17+
throw new ArgumentNullException(nameof(methodInfo));
18+
}
19+
var returnType = methodInfo.ReturnType.GetTypeInfo();
20+
return returnType.IsTaskWithResult();
21+
}
22+
23+
public static Task<object> UnwrapAsyncReturnValue(this IInvocation invocation)
24+
{
25+
if (invocation == null)
26+
{
27+
throw new ArgumentNullException(nameof(invocation));
28+
}
29+
30+
var serviceMethod = invocation.Method ?? invocation.MethodInvocationTarget;
31+
32+
if (!serviceMethod.IsReturnTask())
33+
{
34+
throw new InvalidOperationException("This operation only support asynchronous method.");
35+
}
36+
37+
var returnValue = invocation.ReturnValue;
38+
if (returnValue == null)
39+
{
40+
return null;
41+
}
42+
43+
var returnTypeInfo = returnValue.GetType().GetTypeInfo();
44+
return Unwrap(returnValue, returnTypeInfo);
45+
}
46+
47+
private static async Task<object> Unwrap(object value, TypeInfo valueTypeInfo)
48+
{
49+
object result = null;
50+
51+
if (valueTypeInfo.IsTaskWithResult())
52+
{
53+
// Is there better solution to unwrap ?
54+
result = (object) (await (dynamic) value);
55+
}
56+
else if (value is Task)
57+
{
58+
return null;
59+
}
60+
else
61+
{
62+
result = value;
63+
}
64+
65+
if (result == null)
66+
{
67+
return null;
68+
}
69+
70+
var resultTypeInfo = result.GetType().GetTypeInfo();
71+
if (IsAsyncType(resultTypeInfo))
72+
{
73+
return Unwrap(result, resultTypeInfo);
74+
}
75+
76+
return result;
77+
}
78+
79+
private static bool IsAsyncType(TypeInfo typeInfo)
80+
{
81+
if (typeInfo.IsTask())
82+
{
83+
return true;
84+
}
85+
86+
if (typeInfo.IsTaskWithResult())
87+
{
88+
return true;
89+
}
90+
91+
92+
return false;
93+
}
94+
}
95+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
using System;
2+
using System.Collections.Concurrent;
3+
using System.Reflection;
4+
using System.Threading.Tasks;
5+
6+
namespace EasyCaching.Interceptor.Castle
7+
{
8+
public static class TypeExtensions
9+
{
10+
private static readonly ConcurrentDictionary<TypeInfo, bool> isTaskOfTCache = new ConcurrentDictionary<TypeInfo, bool>();
11+
12+
public static bool IsTaskWithResult(this TypeInfo typeInfo)
13+
{
14+
if (typeInfo == null)
15+
{
16+
throw new ArgumentNullException(nameof(typeInfo));
17+
}
18+
return isTaskOfTCache.GetOrAdd(typeInfo, Info => Info.IsGenericType && typeof(Task).GetTypeInfo().IsAssignableFrom(Info));
19+
}
20+
21+
public static bool IsTask(this TypeInfo typeInfo)
22+
{
23+
if (typeInfo == null)
24+
{
25+
throw new ArgumentNullException(nameof(typeInfo));
26+
}
27+
return typeInfo.AsType() == typeof(Task);
28+
}
29+
30+
}
31+
}

test/EasyCaching.UnitTests/InterceptorTests/CastleInterceptorTest.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -144,10 +144,10 @@ protected virtual async Task Interceptor_Put_With_Task_Method_Should_Succeed()
144144

145145
var key = _keyGenerator.GetCacheKey(method, new object[] { 1, "123" }, "CastleExample");
146146

147-
var value = _cachingProvider.Get<Task<string>>(key);
147+
var value = _cachingProvider.Get<string>(key);
148148

149149
Assert.True(value.HasValue);
150-
Assert.Equal(str, value.Value.Result);
150+
Assert.Equal(str, value.Value);
151151
}
152152

153153
[Fact]

0 commit comments

Comments
 (0)