Browse Source

Refactored and imporved interception abstraction.

pull/96/head
Halil İbrahim Kalkan 10 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) typeof(CastleAbpInterceptorAdapter<>).MakeGenericType(interceptor)
); );
} }
return registrationBuilder; 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.DynamicProxy;
using Volo.Abp.Threading; using Volo.Abp.Threading;
using Volo.ExtensionMethods; using Volo.ExtensionMethods;
@ -29,19 +30,26 @@ namespace Volo.Abp.Castle.DynamicProxy
private void InterceptAsyncMethod(IInvocation invocation) 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 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) 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 Castle.DynamicProxy;
using Volo.Abp.DynamicProxy; using Volo.Abp.DynamicProxy;
using Volo.Abp.Threading; using Volo.Abp.Threading;
namespace Volo.Abp.Castle.DynamicProxy 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) 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(); return Task.CompletedTask;
AsyncHelper.RunSync(() => (Task) Invocation.ReturnValue);
} }
else 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 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 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 System.Threading.Tasks;
using Volo.Abp.DynamicProxy; using Volo.Abp.DynamicProxy;
using Volo.Abp.Threading;
using Volo.DependencyInjection; using Volo.DependencyInjection;
namespace Volo.Abp.Uow namespace Volo.Abp.Uow
{ {
public class UnitOfWorkInterceptor : IAbpInterceptor, IAbpAsyncInterceptor, ITransientDependency public class UnitOfWorkInterceptor : IAbpInterceptor, ITransientDependency
{ {
private readonly IUnitOfWorkManager _unitOfWorkManager; private readonly IUnitOfWorkManager _unitOfWorkManager;
@ -13,19 +12,8 @@ namespace Volo.Abp.Uow
{ {
_unitOfWorkManager = unitOfWorkManager; _unitOfWorkManager = unitOfWorkManager;
} }
public void Intercept(IAbpMethodInvocation invocation)
{
//TODO: Check UOW attribute and other conditions!
using (var uow = _unitOfWorkManager.Begin()) public async Task InterceptAsync(IAbpMethodInvocation invocation)
{
invocation.Proceed();
uow.Complete();
}
}
public async Task InterceptAsync(IAbpAsyncMethodInvocation invocation)
{ {
//TODO: Check UOW attribute and other conditions! //TODO: Check UOW attribute and other conditions!
@ -35,39 +23,5 @@ namespace Volo.Abp.Uow
await uow.CompleteAsync(); 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] [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 //Arrange
@ -32,12 +55,57 @@ namespace Volo.Abp.Autofac.Interception
//Assert //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); result.ShouldBe(42);
target.Logs.Count.ShouldBe(5); target.Logs.Count.ShouldBe(5);
target.Logs[0].ShouldBe("SimpleInterceptor_BeforeInvocation"); target.Logs[0].ShouldBe("SimpleInterceptor_BeforeInvocation");
target.Logs[1].ShouldBe("EnterGetValueAsync"); target.Logs[1].ShouldBe("SimpleInterceptor2_BeforeInvocation");
target.Logs[2].ShouldBe("MiddleGetValueAsync"); target.Logs[2].ShouldBe("ExecutingGetValue");
target.Logs[3].ShouldBe("ExitGetValueAsync"); target.Logs[3].ShouldBe("SimpleInterceptor2_AfterInvocation");
target.Logs[4].ShouldBe("SimpleInterceptor_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) if (typeof(SimpleInterceptionTargetClass) == registration.ImplementationType)
{ {
registration.Interceptors.Add<SimpleInterceptor>(); 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> public abstract class CastleInterceptionTestBase<TStartupModule> : AbpIntegratedTest<TStartupModule>
where TStartupModule : IAbpModule 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 List<string> Logs { get; } = new List<string>();
public virtual void DoIt()
{
Logs.Add("ExecutingDoIt");
}
public virtual int GetValue() public virtual int GetValue()
{ {
Logs.Add("ExecutingGetValue"); Logs.Add("ExecutingGetValue");
@ -18,11 +23,20 @@ namespace Volo.Abp.Castle.DynamicProxy
public virtual async Task<int> GetValueAsync() public virtual async Task<int> GetValueAsync()
{ {
Logs.Add("EnterGetValueAsync"); Logs.Add("EnterGetValueAsync");
await Task.Delay(1); await Task.Delay(5);
Logs.Add("MiddleGetValueAsync"); Logs.Add("MiddleGetValueAsync");
await Task.Delay(1); await Task.Delay(5);
Logs.Add("ExitGetValueAsync"); Logs.Add("ExitGetValueAsync");
return 42; 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 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.TargetObject as ICanLogOnObject)?.Logs?.Add("SimpleInterceptor_BeforeInvocation");
invocation.Proceed(); await invocation.ProceedAsync();
(invocation.TargetObject as ICanLogOnObject)?.Logs?.Add("SimpleInterceptor_AfterInvocation"); (invocation.TargetObject as ICanLogOnObject)?.Logs?.Add("SimpleInterceptor_AfterInvocation");
await Task.Delay(5);
} }
}
//public async Task InterceptAsync(IAbpAsyncMethodInvocation invocation) public class SimpleInterceptor2 : IAbpInterceptor, ITransientDependency
//{ {
// (invocation.TargetObject as ICanLogOnObject)?.Logs?.Add("SimpleInterceptor_BeforeInvocation"); public async Task InterceptAsync(IAbpMethodInvocation invocation)
// await invocation.ProceedAsync(); {
// (invocation.TargetObject as ICanLogOnObject)?.Logs?.Add("SimpleInterceptor_AfterInvocation"); //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