Browse Source

Refactored and imporved interception abstraction.

pull/96/head
Halil İbrahim Kalkan 9 years ago
parent
commit
c6ffb58841
  1. 1
      src/Volo.Abp.Autofac/Autofac/Builder/AbpRegistrationBuilderExtensions.cs
  2. 21
      src/Volo.Abp.Castle.Core/Volo/Abp/Castle/DynamicProxy/CastleAbpAsyncMethodInvocationAdapter.cs
  3. 18
      src/Volo.Abp.Castle.Core/Volo/Abp/Castle/DynamicProxy/CastleAbpInterceptorAdapter.cs
  4. 50
      src/Volo.Abp.Castle.Core/Volo/Abp/Castle/DynamicProxy/CastleAbpMethodInvocationAdapter.cs
  5. 31
      src/Volo.Abp.Castle.Core/Volo/Abp/Castle/DynamicProxy/CastleAbpMethodInvocationAdapterBase.cs
  6. 9
      src/Volo.Abp/Volo/Abp/DynamicProxy/IAbpAsyncInterceptor.cs
  7. 9
      src/Volo.Abp/Volo/Abp/DynamicProxy/IAbpAsyncMethodInvocation.cs
  8. 6
      src/Volo.Abp/Volo/Abp/DynamicProxy/IAbpInterceptor.cs
  9. 18
      src/Volo.Abp/Volo/Abp/DynamicProxy/IAbpMethodInvocation.cs
  10. 18
      src/Volo.Abp/Volo/Abp/DynamicProxy/IAbpMethodInvocationCore.cs
  11. 50
      src/Volo.Abp/Volo/Abp/Uow/UnitOfWorkInterceptor.cs
  12. 76
      test/Volo.Abp.Autofac.Tests/Volo/Abp/Autofac/Interception/Autofac_Interception_Test.cs
  13. 1
      test/Volo.Abp.Castle.Core.Tests/Volo/Abp/Castle/AbpCastleCoreTestModule.cs
  14. 18
      test/Volo.Abp.Castle.Core.Tests/Volo/Abp/Castle/DynamicProxy/CastleInterceptionTestBase.cs
  15. 18
      test/Volo.Abp.Castle.Core.Tests/Volo/Abp/Castle/DynamicProxy/SimpleInterceptionTargetClass.cs
  16. 25
      test/Volo.Abp.Castle.Core.Tests/Volo/Abp/Castle/DynamicProxy/SimpleInterceptor.cs

1
src/Volo.Abp.Autofac/Autofac/Builder/AbpRegistrationBuilderExtensions.cs

@ -54,6 +54,7 @@ namespace Autofac.Builder
typeof(CastleAbpInterceptorAdapter<>).MakeGenericType(interceptor)
);
}
return registrationBuilder;
}
}

21
src/Volo.Abp.Castle.Core/Volo/Abp/Castle/DynamicProxy/CastleAbpAsyncMethodInvocationAdapter.cs

@ -1,21 +0,0 @@
using System.Threading.Tasks;
using Castle.DynamicProxy;
using Volo.Abp.DynamicProxy;
namespace Volo.Abp.Castle.DynamicProxy
{
public class CastleAbpAsyncMethodInvocationAdapter : CastleAbpMethodInvocationAdapterBase, IAbpAsyncMethodInvocation
{
public CastleAbpAsyncMethodInvocationAdapter(IInvocation invocation)
: base(invocation)
{
}
public Task ProceedAsync()
{
Invocation.Proceed();
return (Task)Invocation.ReturnValue;
}
}
}

18
src/Volo.Abp.Castle.Core/Volo/Abp/Castle/DynamicProxy/CastleAbpInterceptorAdapter.cs

@ -1,4 +1,5 @@
using Castle.DynamicProxy;
using System.Threading.Tasks;
using Castle.DynamicProxy;
using Volo.Abp.DynamicProxy;
using Volo.Abp.Threading;
using Volo.ExtensionMethods;
@ -29,19 +30,26 @@ namespace Volo.Abp.Castle.DynamicProxy
private void InterceptAsyncMethod(IInvocation invocation)
{
if (_abpInterceptor is IAbpAsyncInterceptor)
if (invocation.Method.ReturnType == typeof(Task))
{
_abpInterceptor.As<IAbpAsyncInterceptor>().InterceptAsync(new CastleAbpAsyncMethodInvocationAdapter(invocation));
invocation.ReturnValue = _abpInterceptor.InterceptAsync(new CastleAbpMethodInvocationAdapter(invocation));
}
else
{
_abpInterceptor.Intercept(new CastleAbpMethodInvocationAdapter(invocation));
var interceptResult = _abpInterceptor.InterceptAsync(new CastleAbpMethodInvocationAdapter(invocation));
invocation.ReturnValue = InternalAsyncHelper.CallAwaitTaskWithPostActionAndFinallyAndGetResult(
invocation.Method.ReturnType.GenericTypeArguments[0],
invocation.ReturnValue, //TODO: Can not change return value in that case !!! Create an interceptor that changes return value, for test purposes!
() => interceptResult,
exception => { }
);
}
}
private void InterceptSyncMethod(IInvocation invocation)
{
_abpInterceptor.Intercept(new CastleAbpMethodInvocationAdapter(invocation));
AsyncHelper.RunSync(() => _abpInterceptor.InterceptAsync(new CastleAbpMethodInvocationAdapter(invocation)));
}
}
}

50
src/Volo.Abp.Castle.Core/Volo/Abp/Castle/DynamicProxy/CastleAbpMethodInvocationAdapter.cs

@ -1,28 +1,62 @@
using System.Threading.Tasks;
using System;
using System.Reflection;
using System.Threading.Tasks;
using Castle.DynamicProxy;
using Volo.Abp.DynamicProxy;
using Volo.Abp.Threading;
namespace Volo.Abp.Castle.DynamicProxy
{
public class CastleAbpMethodInvocationAdapter : CastleAbpMethodInvocationAdapterBase, IAbpMethodInvocation
public class CastleAbpMethodInvocationAdapter : IAbpMethodInvocation
{
public object[] Arguments => Invocation.Arguments;
public Type[] GenericArguments => Invocation.GenericArguments;
public object TargetObject => Invocation.InvocationTarget;
public MethodInfo Method => Invocation.MethodInvocationTarget;
public object ReturnValue
{
get => Invocation.ReturnValue;
set => Invocation.ReturnValue = value;
}
protected IInvocation Invocation { get; }
public CastleAbpMethodInvocationAdapter(IInvocation invocation)
: base(invocation)
{
Invocation = invocation;
}
public Task ProceedAsync()
{
return Invocation.Method.IsAsync()
? RunAsync()
: RunSync();
}
public void Proceed()
private Task RunAsync()
{
if (Invocation.Method.IsAsync())
Invocation.Proceed();
return (Task) Invocation.ReturnValue;
}
private Task RunSync()
{
Invocation.Proceed();
if (Method.ReturnType == typeof(void))
{
Invocation.Proceed();
AsyncHelper.RunSync(() => (Task) Invocation.ReturnValue);
return Task.CompletedTask;
}
else
{
Invocation.Proceed();
return (Task) typeof(Task)
.GetMethod("FromResult", BindingFlags.Static | BindingFlags.Public)
.MakeGenericMethod(Method.ReturnType)
.Invoke(null, new object[] {Invocation.ReturnValue});
}
}
}

31
src/Volo.Abp.Castle.Core/Volo/Abp/Castle/DynamicProxy/CastleAbpMethodInvocationAdapterBase.cs

@ -1,31 +0,0 @@
using System;
using System.Reflection;
using Castle.DynamicProxy;
using Volo.Abp.DynamicProxy;
namespace Volo.Abp.Castle.DynamicProxy
{
public abstract class CastleAbpMethodInvocationAdapterBase : IAbpMethodInvocationCore
{
public object[] Arguments => Invocation.Arguments;
public Type[] GenericArguments => Invocation.GenericArguments;
public object TargetObject => Invocation.InvocationTarget;
public MethodInfo Method => Invocation.MethodInvocationTarget;
public object ReturnValue
{
get => Invocation.ReturnValue;
set => Invocation.ReturnValue = value;
}
protected IInvocation Invocation { get; }
protected CastleAbpMethodInvocationAdapterBase(IInvocation invocation)
{
Invocation = invocation;
}
}
}

9
src/Volo.Abp/Volo/Abp/DynamicProxy/IAbpAsyncInterceptor.cs

@ -1,9 +0,0 @@
using System.Threading.Tasks;
namespace Volo.Abp.DynamicProxy
{
public interface IAbpAsyncInterceptor
{
Task InterceptAsync(IAbpAsyncMethodInvocation invocation);
}
}

9
src/Volo.Abp/Volo/Abp/DynamicProxy/IAbpAsyncMethodInvocation.cs

@ -1,9 +0,0 @@
using System.Threading.Tasks;
namespace Volo.Abp.DynamicProxy
{
public interface IAbpAsyncMethodInvocation: IAbpMethodInvocationCore
{
Task ProceedAsync();
}
}

6
src/Volo.Abp/Volo/Abp/DynamicProxy/IAbpInterceptor.cs

@ -1,7 +1,9 @@
namespace Volo.Abp.DynamicProxy
using System.Threading.Tasks;
namespace Volo.Abp.DynamicProxy
{
public interface IAbpInterceptor
{
void Intercept(IAbpMethodInvocation invocation);
Task InterceptAsync(IAbpMethodInvocation invocation);
}
}

18
src/Volo.Abp/Volo/Abp/DynamicProxy/IAbpMethodInvocation.cs

@ -1,7 +1,21 @@
using System;
using System.Reflection;
using System.Threading.Tasks;
namespace Volo.Abp.DynamicProxy
{
public interface IAbpMethodInvocation: IAbpMethodInvocationCore
public interface IAbpMethodInvocation
{
void Proceed();
object[] Arguments { get; }
Type[] GenericArguments { get; }
object TargetObject { get; }
MethodInfo Method { get; }
object ReturnValue { get; set; }
Task ProceedAsync();
}
}

18
src/Volo.Abp/Volo/Abp/DynamicProxy/IAbpMethodInvocationCore.cs

@ -1,18 +0,0 @@
using System;
using System.Reflection;
namespace Volo.Abp.DynamicProxy
{
public interface IAbpMethodInvocationCore
{
object[] Arguments { get; }
Type[] GenericArguments { get; }
object TargetObject { get; }
MethodInfo Method { get; }
object ReturnValue { get; set; }
}
}

50
src/Volo.Abp/Volo/Abp/Uow/UnitOfWorkInterceptor.cs

@ -1,11 +1,10 @@
using System.Threading.Tasks;
using Volo.Abp.DynamicProxy;
using Volo.Abp.Threading;
using Volo.DependencyInjection;
namespace Volo.Abp.Uow
{
public class UnitOfWorkInterceptor : IAbpInterceptor, IAbpAsyncInterceptor, ITransientDependency
public class UnitOfWorkInterceptor : IAbpInterceptor, ITransientDependency
{
private readonly IUnitOfWorkManager _unitOfWorkManager;
@ -13,19 +12,8 @@ namespace Volo.Abp.Uow
{
_unitOfWorkManager = unitOfWorkManager;
}
public void Intercept(IAbpMethodInvocation invocation)
{
//TODO: Check UOW attribute and other conditions!
using (var uow = _unitOfWorkManager.Begin())
{
invocation.Proceed();
uow.Complete();
}
}
public async Task InterceptAsync(IAbpAsyncMethodInvocation invocation)
public async Task InterceptAsync(IAbpMethodInvocation invocation)
{
//TODO: Check UOW attribute and other conditions!
@ -35,39 +23,5 @@ namespace Volo.Abp.Uow
await uow.CompleteAsync();
}
}
private void PerformAsyncUow(IAbpMethodInvocation invocation)
{
var uow = _unitOfWorkManager.Begin();
try
{
invocation.Proceed();
}
catch
{
uow.Dispose();
throw;
}
if (invocation.Method.ReturnType == typeof(Task))
{
invocation.ReturnValue = InternalAsyncHelper.AwaitTaskWithPostActionAndFinally(
(Task)invocation.ReturnValue,
async () => await uow.CompleteAsync(),
exception => uow.Dispose()
);
}
else //Task<TResult>
{
invocation.ReturnValue = InternalAsyncHelper.CallAwaitTaskWithPostActionAndFinallyAndGetResult(
invocation.Method.ReturnType.GenericTypeArguments[0],
invocation.ReturnValue,
async () => await uow.CompleteAsync(),
exception => uow.Dispose()
);
}
}
}
}

76
test/Volo.Abp.Autofac.Tests/Volo/Abp/Autofac/Interception/Autofac_Interception_Test.cs

@ -20,7 +20,30 @@ namespace Volo.Abp.Autofac.Interception
}
[Fact]
public async Task Should_Intercept_Async_Methods()
public async Task Should_Intercept_Async_Method_Without_Return_Value()
{
//Arrange
var target = ServiceProvider.GetService<SimpleInterceptionTargetClass>();
//Act
await target.DoItAsync();
//Assert
target.Logs.Count.ShouldBe(7);
target.Logs[0].ShouldBe("SimpleInterceptor_BeforeInvocation");
target.Logs[1].ShouldBe("SimpleInterceptor2_BeforeInvocation");
target.Logs[2].ShouldBe("EnterDoItAsync");
target.Logs[3].ShouldBe("MiddleDoItAsync");
target.Logs[4].ShouldBe("ExitDoItAsync");
target.Logs[5].ShouldBe("SimpleInterceptor2_AfterInvocation");
target.Logs[6].ShouldBe("SimpleInterceptor_AfterInvocation");
}
[Fact]
public async Task Should_Intercept_Async_Method_With_Return_Value()
{
//Arrange
@ -32,12 +55,57 @@ namespace Volo.Abp.Autofac.Interception
//Assert
result.ShouldBe(42);
target.Logs.Count.ShouldBe(7);
target.Logs[0].ShouldBe("SimpleInterceptor_BeforeInvocation");
target.Logs[1].ShouldBe("SimpleInterceptor2_BeforeInvocation");
target.Logs[2].ShouldBe("EnterGetValueAsync");
target.Logs[3].ShouldBe("MiddleGetValueAsync");
target.Logs[4].ShouldBe("ExitGetValueAsync");
target.Logs[5].ShouldBe("SimpleInterceptor2_AfterInvocation");
target.Logs[6].ShouldBe("SimpleInterceptor_AfterInvocation");
}
[Fact]
public void Should_Intercept_Sync_Method_Without_Return_Value()
{
//Arrange
var target = ServiceProvider.GetService<SimpleInterceptionTargetClass>();
//Act
target.DoIt();
//Assert
target.Logs.Count.ShouldBe(5);
target.Logs[0].ShouldBe("SimpleInterceptor_BeforeInvocation");
target.Logs[1].ShouldBe("SimpleInterceptor2_BeforeInvocation");
target.Logs[2].ShouldBe("ExecutingDoIt");
target.Logs[3].ShouldBe("SimpleInterceptor2_AfterInvocation");
target.Logs[4].ShouldBe("SimpleInterceptor_AfterInvocation");
}
[Fact]
public void Should_Intercept_Sync_Method_With_Return_Value()
{
//Arrange
var target = ServiceProvider.GetService<SimpleInterceptionTargetClass>();
//Act
var result = target.GetValue();
//Assert
result.ShouldBe(42);
target.Logs.Count.ShouldBe(5);
target.Logs[0].ShouldBe("SimpleInterceptor_BeforeInvocation");
target.Logs[1].ShouldBe("EnterGetValueAsync");
target.Logs[2].ShouldBe("MiddleGetValueAsync");
target.Logs[3].ShouldBe("ExitGetValueAsync");
target.Logs[1].ShouldBe("SimpleInterceptor2_BeforeInvocation");
target.Logs[2].ShouldBe("ExecutingGetValue");
target.Logs[3].ShouldBe("SimpleInterceptor2_AfterInvocation");
target.Logs[4].ShouldBe("SimpleInterceptor_AfterInvocation");
}
}

1
test/Volo.Abp.Castle.Core.Tests/Volo/Abp/Castle/AbpCastleCoreTestModule.cs

@ -23,6 +23,7 @@ namespace Volo.Abp.Castle
if (typeof(SimpleInterceptionTargetClass) == registration.ImplementationType)
{
registration.Interceptors.Add<SimpleInterceptor>();
registration.Interceptors.Add<SimpleInterceptor2>();
}
}
}

18
test/Volo.Abp.Castle.Core.Tests/Volo/Abp/Castle/DynamicProxy/CastleInterceptionTestBase.cs

@ -11,24 +11,6 @@ namespace Volo.Abp.Castle.DynamicProxy
public abstract class CastleInterceptionTestBase<TStartupModule> : AbpIntegratedTest<TStartupModule>
where TStartupModule : IAbpModule
{
[Fact]
public void Should_Intercept_Sync_Methods()
{
//Arrange
var target = ServiceProvider.GetService<SimpleInterceptionTargetClass>();
//Act
var result = target.GetValue();
//Assert
result.ShouldBe(42);
target.Logs.Count.ShouldBe(3);
target.Logs[0].ShouldBe("SimpleInterceptor_BeforeInvocation");
target.Logs[1].ShouldBe("ExecutingGetValue");
target.Logs[2].ShouldBe("SimpleInterceptor_AfterInvocation");
}
}
}

18
test/Volo.Abp.Castle.Core.Tests/Volo/Abp/Castle/DynamicProxy/SimpleInterceptionTargetClass.cs

@ -9,6 +9,11 @@ namespace Volo.Abp.Castle.DynamicProxy
{
public List<string> Logs { get; } = new List<string>();
public virtual void DoIt()
{
Logs.Add("ExecutingDoIt");
}
public virtual int GetValue()
{
Logs.Add("ExecutingGetValue");
@ -18,11 +23,20 @@ namespace Volo.Abp.Castle.DynamicProxy
public virtual async Task<int> GetValueAsync()
{
Logs.Add("EnterGetValueAsync");
await Task.Delay(1);
await Task.Delay(5);
Logs.Add("MiddleGetValueAsync");
await Task.Delay(1);
await Task.Delay(5);
Logs.Add("ExitGetValueAsync");
return 42;
}
public virtual async Task DoItAsync()
{
Logs.Add("EnterDoItAsync");
await Task.Delay(5);
Logs.Add("MiddleDoItAsync");
await Task.Delay(5);
Logs.Add("ExitDoItAsync");
}
}
}

25
test/Volo.Abp.Castle.Core.Tests/Volo/Abp/Castle/DynamicProxy/SimpleInterceptor.cs

@ -5,20 +5,27 @@ using Volo.DependencyInjection;
namespace Volo.Abp.Castle.DynamicProxy
{
public class SimpleInterceptor : IAbpInterceptor, /*IAbpAsyncInterceptor,*/ ITransientDependency
public class SimpleInterceptor : IAbpInterceptor, ITransientDependency
{
public void Intercept(IAbpMethodInvocation invocation)
public async Task InterceptAsync(IAbpMethodInvocation invocation)
{
//await Task.Delay(5);
(invocation.TargetObject as ICanLogOnObject)?.Logs?.Add("SimpleInterceptor_BeforeInvocation");
invocation.Proceed();
await invocation.ProceedAsync();
(invocation.TargetObject as ICanLogOnObject)?.Logs?.Add("SimpleInterceptor_AfterInvocation");
await Task.Delay(5);
}
}
//public async Task InterceptAsync(IAbpAsyncMethodInvocation invocation)
//{
// (invocation.TargetObject as ICanLogOnObject)?.Logs?.Add("SimpleInterceptor_BeforeInvocation");
// await invocation.ProceedAsync();
// (invocation.TargetObject as ICanLogOnObject)?.Logs?.Add("SimpleInterceptor_AfterInvocation");
//}
public class SimpleInterceptor2 : IAbpInterceptor, ITransientDependency
{
public async Task InterceptAsync(IAbpMethodInvocation invocation)
{
//await Task.Delay(5);
(invocation.TargetObject as ICanLogOnObject)?.Logs?.Add("SimpleInterceptor2_BeforeInvocation");
await invocation.ProceedAsync();
(invocation.TargetObject as ICanLogOnObject)?.Logs?.Add("SimpleInterceptor2_AfterInvocation");
await Task.Delay(5);
}
}
}
Loading…
Cancel
Save