diff --git a/src/Castle.Core.Tests/DynamicProxy.Tests/AsyncInterceptorTestCase.cs b/src/Castle.Core.Tests/DynamicProxy.Tests/AsyncInterceptorTestCase.cs new file mode 100644 index 0000000000..9d5838937a --- /dev/null +++ b/src/Castle.Core.Tests/DynamicProxy.Tests/AsyncInterceptorTestCase.cs @@ -0,0 +1,44 @@ +// Copyright 2004-2016 Castle Project - http://www.castleproject.org/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Castle.DynamicProxy.Tests +{ + using System; + using System.Linq; + using System.Threading.Tasks; + + using Castle.DynamicProxy.Tests.Classes; + using Castle.DynamicProxy.Tests.Interfaces; + using Castle.DynamicProxy.Tests.Interceptors; + + using NUnit.Framework; + + [TestFixture] + public class AsyncInterceptorTestCase : BasePEVerifyTestCase + { + [Test] + public async Task Should_Intercept_Asynchronous_Methods_With_An_Async_Operations_Prior_To_Calling_Proceed() + { + // Arrange + IInterfaceWithAsynchronousMethod target = new ClassWithAsynchronousMethod(); + IInterceptor interceptor = new AsyncInterceptor(); + + IInterfaceWithAsynchronousMethod proxy = + generator.CreateInterfaceProxyWithTargetInterface(target, interceptor); + + // Act + await proxy.Method().ConfigureAwait(false); + } + } +} \ No newline at end of file diff --git a/src/Castle.Core.Tests/DynamicProxy.Tests/BasicClassProxyTestCase.cs b/src/Castle.Core.Tests/DynamicProxy.Tests/BasicClassProxyTestCase.cs index a481147711..983bd17e86 100644 --- a/src/Castle.Core.Tests/DynamicProxy.Tests/BasicClassProxyTestCase.cs +++ b/src/Castle.Core.Tests/DynamicProxy.Tests/BasicClassProxyTestCase.cs @@ -448,7 +448,7 @@ public void Finalize_method_is_proxied_even_though_its_not_the_best_idea_ever() public class ResultModifierInterceptor : StandardInterceptor { - protected override void PostProceed(IInvocation invocation) + protected override void PostProceed(IInvocation invocation, InvocationDelegate proceed) { object returnValue = invocation.ReturnValue; diff --git a/src/Castle.Core.Tests/DynamicProxy.Tests/ChangeProxyTargetInterceptor.cs b/src/Castle.Core.Tests/DynamicProxy.Tests/ChangeProxyTargetInterceptor.cs index f1df07f8eb..74660650ae 100644 --- a/src/Castle.Core.Tests/DynamicProxy.Tests/ChangeProxyTargetInterceptor.cs +++ b/src/Castle.Core.Tests/DynamicProxy.Tests/ChangeProxyTargetInterceptor.cs @@ -25,12 +25,12 @@ public ChangeProxyTargetInterceptor(object target) this.target = target; } - public void Intercept(IInvocation invocation) + public void Intercept(IInvocation invocation, InvocationDelegate proceed) { var targetAccessor = invocation.Proxy as IProxyTargetAccessor; Assert.IsNotNull(targetAccessor); targetAccessor.DynProxySetTarget(target); - invocation.Proceed(); + proceed(invocation); } } } \ No newline at end of file diff --git a/src/Castle.Core.Tests/DynamicProxy.Tests/ChangeProxyTargetTestCase.cs b/src/Castle.Core.Tests/DynamicProxy.Tests/ChangeProxyTargetTestCase.cs index b1f76db15f..00d5e4b1a8 100644 --- a/src/Castle.Core.Tests/DynamicProxy.Tests/ChangeProxyTargetTestCase.cs +++ b/src/Castle.Core.Tests/DynamicProxy.Tests/ChangeProxyTargetTestCase.cs @@ -28,7 +28,7 @@ public LazyInterceptorV1(Lazy lazyTarget) private Lazy LazyTarget { get; } - public void Intercept(IInvocation invocation) + public void Intercept(IInvocation invocation, InvocationDelegate proceed) { var target = invocation.InvocationTarget as T; if (target == null) @@ -37,7 +37,7 @@ public void Intercept(IInvocation invocation) ((IProxyTargetAccessor)invocation.Proxy).DynProxySetTarget(LazyTarget.Value); } - invocation.Proceed(); + proceed(invocation); } } @@ -51,7 +51,7 @@ public LazyInterceptorV2(Lazy lazyTarget) private Lazy LazyTarget { get; } - public void Intercept(IInvocation invocation) + public void Intercept(IInvocation invocation, InvocationDelegate proceed) { var target = invocation.InvocationTarget as T; if (target == null) @@ -62,7 +62,7 @@ public void Intercept(IInvocation invocation) #pragma warning restore CS0618 // obsolete } - invocation.Proceed(); + proceed(invocation); } } diff --git a/src/Castle.Core.Tests/DynamicProxy.Tests/Classes/ClassWithAsynchronousMethod.cs b/src/Castle.Core.Tests/DynamicProxy.Tests/Classes/ClassWithAsynchronousMethod.cs new file mode 100644 index 0000000000..8b217ccea8 --- /dev/null +++ b/src/Castle.Core.Tests/DynamicProxy.Tests/Classes/ClassWithAsynchronousMethod.cs @@ -0,0 +1,38 @@ +// Copyright 2004-2010 Castle Project - http://www.castleproject.org/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Castle.DynamicProxy.Tests.Classes +{ + using System; + using System.Threading; + using System.Threading.Tasks; + + using Castle.DynamicProxy.Tests.Interfaces; + + public class ClassWithAsynchronousMethod : IInterfaceWithAsynchronousMethod + { + public async Task Method() + { + Console.WriteLine( + $"Before Await ClassWithAsynchronousMethod:Method ThreadId='{Thread.CurrentThread.ManagedThreadId}'.", + Thread.CurrentThread.ManagedThreadId); + + await Task.Delay(10).ConfigureAwait(false); + + Console.WriteLine( + $"After Await ClassWithAsynchronousMethod:Method ThreadId='{Thread.CurrentThread.ManagedThreadId}'.", + Thread.CurrentThread.ManagedThreadId); + } + } +} \ No newline at end of file diff --git a/src/Castle.Core.Tests/DynamicProxy.Tests/InterceptorSelectorTargetTypeTestCase.cs b/src/Castle.Core.Tests/DynamicProxy.Tests/InterceptorSelectorTargetTypeTestCase.cs index 2ea9956218..6d3a6760cb 100644 --- a/src/Castle.Core.Tests/DynamicProxy.Tests/InterceptorSelectorTargetTypeTestCase.cs +++ b/src/Castle.Core.Tests/DynamicProxy.Tests/InterceptorSelectorTargetTypeTestCase.cs @@ -173,7 +173,7 @@ public sealed class Interceptor : IInterceptor { public Type ReceivedTargetType { get; private set; } - public void Intercept(IInvocation invocation) + public void Intercept(IInvocation invocation, InvocationDelegate proceed) { ReceivedTargetType = invocation.TargetType; } diff --git a/src/Castle.Core.Tests/DynamicProxy.Tests/Interceptors/AddTwoInterceptor.cs b/src/Castle.Core.Tests/DynamicProxy.Tests/Interceptors/AddTwoInterceptor.cs index 8fc7ab9f14..4ea8f21e53 100644 --- a/src/Castle.Core.Tests/DynamicProxy.Tests/Interceptors/AddTwoInterceptor.cs +++ b/src/Castle.Core.Tests/DynamicProxy.Tests/Interceptors/AddTwoInterceptor.cs @@ -23,9 +23,9 @@ public class AddTwoInterceptor : IInterceptor { #region IInterceptor Members - public void Intercept(IInvocation invocation) + public void Intercept(IInvocation invocation, InvocationDelegate proceed) { - invocation.Proceed(); + proceed(invocation); var ret = (int) invocation.ReturnValue; ret += 2; invocation.ReturnValue = ret; diff --git a/src/Castle.Core.Tests/DynamicProxy.Tests/Interceptors/AssertCanChangeTargetInterceptor.cs b/src/Castle.Core.Tests/DynamicProxy.Tests/Interceptors/AssertCanChangeTargetInterceptor.cs index 9a4fef43b4..1bf2cf3ac5 100644 --- a/src/Castle.Core.Tests/DynamicProxy.Tests/Interceptors/AssertCanChangeTargetInterceptor.cs +++ b/src/Castle.Core.Tests/DynamicProxy.Tests/Interceptors/AssertCanChangeTargetInterceptor.cs @@ -20,10 +20,10 @@ public class AssertCanChangeTargetInterceptor : IInterceptor { #region IInterceptor Members - public void Intercept(IInvocation invocation) + public void Intercept(IInvocation invocation, InvocationDelegate proceed) { Assert.IsInstanceOf(typeof (IChangeProxyTarget), invocation); - invocation.Proceed(); + proceed(invocation); } #endregion diff --git a/src/Castle.Core.Tests/DynamicProxy.Tests/Interceptors/AssertCannotChangeTargetInterceptor.cs b/src/Castle.Core.Tests/DynamicProxy.Tests/Interceptors/AssertCannotChangeTargetInterceptor.cs index a0ab7f2e1f..fa1ce00800 100644 --- a/src/Castle.Core.Tests/DynamicProxy.Tests/Interceptors/AssertCannotChangeTargetInterceptor.cs +++ b/src/Castle.Core.Tests/DynamicProxy.Tests/Interceptors/AssertCannotChangeTargetInterceptor.cs @@ -18,10 +18,10 @@ namespace Castle.DynamicProxy.Tests.Interceptors public class AssertCannotChangeTargetInterceptor : IInterceptor { - public void Intercept(IInvocation invocation) + public void Intercept(IInvocation invocation, InvocationDelegate proceed) { Assert.IsNotInstanceOf(invocation); - invocation.Proceed(); + proceed(invocation); } } } diff --git a/src/Castle.Core.Tests/DynamicProxy.Tests/Interceptors/AsyncInterceptor.cs b/src/Castle.Core.Tests/DynamicProxy.Tests/Interceptors/AsyncInterceptor.cs new file mode 100644 index 0000000000..424c03fea7 --- /dev/null +++ b/src/Castle.Core.Tests/DynamicProxy.Tests/Interceptors/AsyncInterceptor.cs @@ -0,0 +1,40 @@ +// Copyright 2004-2010 Castle Project - http://www.castleproject.org/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Castle.DynamicProxy.Tests.Interceptors +{ + using System.Threading.Tasks; + + public class AsyncInterceptor : IInterceptor + { + public void Intercept(IInvocation invocation, InvocationDelegate proceed) + { + invocation.ReturnValue = InterceptAsyncMethod(invocation, proceed); + } + + private static async Task InterceptAsyncMethod(IInvocation invocation, InvocationDelegate proceed) + { + // It all falls down when executing async before calling Proceed(). + await Task.Delay(10).ConfigureAwait(false); + + proceed(invocation); + + // Hmmmmm, now with it simplified down to this, I see the glaring hole that is the return value being set + // in two situations. + Task returnValue = (Task)invocation.ReturnValue; + + await returnValue.ConfigureAwait(false); + } + } +} \ No newline at end of file diff --git a/src/Castle.Core.Tests/DynamicProxy.Tests/Interceptors/CallCountingInterceptor.cs b/src/Castle.Core.Tests/DynamicProxy.Tests/Interceptors/CallCountingInterceptor.cs index dd0cc2cb21..e878800039 100644 --- a/src/Castle.Core.Tests/DynamicProxy.Tests/Interceptors/CallCountingInterceptor.cs +++ b/src/Castle.Core.Tests/DynamicProxy.Tests/Interceptors/CallCountingInterceptor.cs @@ -30,10 +30,10 @@ public int Count #region IInterceptor Members - public void Intercept(IInvocation invocation) + public void Intercept(IInvocation invocation, InvocationDelegate proceed) { count++; - invocation.Proceed(); + proceed(invocation); } #endregion diff --git a/src/Castle.Core.Tests/DynamicProxy.Tests/Interceptors/ChangeTargetInterceptor.cs b/src/Castle.Core.Tests/DynamicProxy.Tests/Interceptors/ChangeTargetInterceptor.cs index 2f036eaf56..15907a7389 100644 --- a/src/Castle.Core.Tests/DynamicProxy.Tests/Interceptors/ChangeTargetInterceptor.cs +++ b/src/Castle.Core.Tests/DynamicProxy.Tests/Interceptors/ChangeTargetInterceptor.cs @@ -23,11 +23,11 @@ public ChangeTargetInterceptor(object target) this.target = target; } - public void Intercept(IInvocation invocation) + public void Intercept(IInvocation invocation, InvocationDelegate proceed) { IChangeProxyTarget changeTarget = (IChangeProxyTarget) invocation; changeTarget.ChangeInvocationTarget(target); - invocation.Proceed(); + proceed(invocation); } } } \ No newline at end of file diff --git a/src/Castle.Core.Tests/DynamicProxy.Tests/Interceptors/DoNothingInterceptor.cs b/src/Castle.Core.Tests/DynamicProxy.Tests/Interceptors/DoNothingInterceptor.cs index 340024d844..ed6142916b 100644 --- a/src/Castle.Core.Tests/DynamicProxy.Tests/Interceptors/DoNothingInterceptor.cs +++ b/src/Castle.Core.Tests/DynamicProxy.Tests/Interceptors/DoNothingInterceptor.cs @@ -16,7 +16,7 @@ namespace Castle.DynamicProxy.Tests.Interceptors { public class DoNothingInterceptor : IInterceptor { - public void Intercept(IInvocation invocation) + public void Intercept(IInvocation invocation, InvocationDelegate proceed) { } } diff --git a/src/Castle.Core.Tests/DynamicProxy.Tests/Interceptors/IInterfaceWithAsynchronousMethod.cs b/src/Castle.Core.Tests/DynamicProxy.Tests/Interceptors/IInterfaceWithAsynchronousMethod.cs new file mode 100644 index 0000000000..eb86bb4ea1 --- /dev/null +++ b/src/Castle.Core.Tests/DynamicProxy.Tests/Interceptors/IInterfaceWithAsynchronousMethod.cs @@ -0,0 +1,23 @@ +// Copyright 2004-2010 Castle Project - http://www.castleproject.org/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Castle.DynamicProxy.Tests.Interfaces +{ + using System.Threading.Tasks; + + public interface IInterfaceWithAsynchronousMethod + { + Task Method(); + } +} \ No newline at end of file diff --git a/src/Castle.Core.Tests/DynamicProxy.Tests/Interceptors/KeepDataInterceptor.cs b/src/Castle.Core.Tests/DynamicProxy.Tests/Interceptors/KeepDataInterceptor.cs index 01d05da68e..60bbb17676 100644 --- a/src/Castle.Core.Tests/DynamicProxy.Tests/Interceptors/KeepDataInterceptor.cs +++ b/src/Castle.Core.Tests/DynamicProxy.Tests/Interceptors/KeepDataInterceptor.cs @@ -26,14 +26,14 @@ public IInvocation Invocation get { return invocation; } } - public void Intercept(IInvocation invocation) + public void Intercept(IInvocation invocation, InvocationDelegate proceed) { this.invocation = invocation; var concreteMethod = invocation.GetConcreteMethod(); if (invocation.MethodInvocationTarget != null) { - invocation.Proceed(); + proceed(invocation); } else if (concreteMethod.ReturnType.GetTypeInfo().IsValueType && !concreteMethod.ReturnType.Equals(typeof(void))) // ensure valid return value diff --git a/src/Castle.Core.Tests/DynamicProxy.Tests/Interceptors/LogInvocationInterceptor.cs b/src/Castle.Core.Tests/DynamicProxy.Tests/Interceptors/LogInvocationInterceptor.cs index 073942b710..18e759bdcd 100644 --- a/src/Castle.Core.Tests/DynamicProxy.Tests/Interceptors/LogInvocationInterceptor.cs +++ b/src/Castle.Core.Tests/DynamicProxy.Tests/Interceptors/LogInvocationInterceptor.cs @@ -27,18 +27,18 @@ public class LogInvocationInterceptor : StandardInterceptor public bool Proceed = true; - protected override void PreProceed(IInvocation invocation) + protected override void PreProceed(IInvocation invocation, InvocationDelegate proceed) { invocations.Add(invocation.Method.Name); sb.Append(String.Format("{0} ", invocation.Method.Name)); } - protected override void PerformProceed (IInvocation invocation) + protected override void PerformProceed (IInvocation invocation, InvocationDelegate proceed) { if (Proceed) { - base.PerformProceed (invocation); + base.PerformProceed (invocation, proceed); } else if (invocation.Method.ReturnType.GetTypeInfo().IsValueType && invocation.Method.ReturnType != typeof (void)) { diff --git a/src/Castle.Core.Tests/DynamicProxy.Tests/Interceptors/ProceedNTimesInterceptor.cs b/src/Castle.Core.Tests/DynamicProxy.Tests/Interceptors/ProceedNTimesInterceptor.cs index 396c2b6a13..2581d3767b 100644 --- a/src/Castle.Core.Tests/DynamicProxy.Tests/Interceptors/ProceedNTimesInterceptor.cs +++ b/src/Castle.Core.Tests/DynamicProxy.Tests/Interceptors/ProceedNTimesInterceptor.cs @@ -23,13 +23,13 @@ public ProceedNTimesInterceptor(int retries) this.retries = retries; } - public void Intercept(IInvocation invocation) + public void Intercept(IInvocation invocation, InvocationDelegate proceed) { for (var i = 0; i < retries; i++) { try { - invocation.Proceed(); + proceed(invocation); } catch { diff --git a/src/Castle.Core.Tests/DynamicProxy.Tests/Interceptors/ProceedOnTypeInterceptor.cs b/src/Castle.Core.Tests/DynamicProxy.Tests/Interceptors/ProceedOnTypeInterceptor.cs index 2f88bc2bd3..1a65ea6225 100644 --- a/src/Castle.Core.Tests/DynamicProxy.Tests/Interceptors/ProceedOnTypeInterceptor.cs +++ b/src/Castle.Core.Tests/DynamicProxy.Tests/Interceptors/ProceedOnTypeInterceptor.cs @@ -27,12 +27,12 @@ public ProceedOnTypeInterceptor(Type type) this.type = type; } - public void Intercept(IInvocation invocation) + public void Intercept(IInvocation invocation, InvocationDelegate proceed) { type = typeof(IBarFoo); if (invocation.Method.DeclaringType != type) { - invocation.Proceed(); + proceed(invocation); } } diff --git a/src/Castle.Core.Tests/DynamicProxy.Tests/Interceptors/RequiredParamInterceptor.cs b/src/Castle.Core.Tests/DynamicProxy.Tests/Interceptors/RequiredParamInterceptor.cs index 15eb272121..9cd2aa4a77 100644 --- a/src/Castle.Core.Tests/DynamicProxy.Tests/Interceptors/RequiredParamInterceptor.cs +++ b/src/Castle.Core.Tests/DynamicProxy.Tests/Interceptors/RequiredParamInterceptor.cs @@ -21,7 +21,7 @@ namespace Castle.DynamicProxy.Tests.Interceptors public class RequiredParamInterceptor : IInterceptor { - public void Intercept(IInvocation invocation) + public void Intercept(IInvocation invocation, InvocationDelegate proceed) { ParameterInfo[] parameters = invocation.Method.GetParameters(); @@ -42,7 +42,7 @@ public void Intercept(IInvocation invocation) } } - invocation.Proceed(); + proceed(invocation); } } } diff --git a/src/Castle.Core.Tests/DynamicProxy.Tests/Interceptors/SetArgumentValueInterceptor.cs b/src/Castle.Core.Tests/DynamicProxy.Tests/Interceptors/SetArgumentValueInterceptor.cs index 427c73acf5..fd1366b494 100644 --- a/src/Castle.Core.Tests/DynamicProxy.Tests/Interceptors/SetArgumentValueInterceptor.cs +++ b/src/Castle.Core.Tests/DynamicProxy.Tests/Interceptors/SetArgumentValueInterceptor.cs @@ -25,7 +25,7 @@ public SetArgumentValueInterceptor(int index, object value) this.value = value; } - public void Intercept(IInvocation invocation) + public void Intercept(IInvocation invocation, InvocationDelegate proceed) { invocation.SetArgumentValue(index, value); } diff --git a/src/Castle.Core.Tests/DynamicProxy.Tests/Interceptors/SetReturnValueInterceptor.cs b/src/Castle.Core.Tests/DynamicProxy.Tests/Interceptors/SetReturnValueInterceptor.cs index 66ff270960..e7f93893c3 100644 --- a/src/Castle.Core.Tests/DynamicProxy.Tests/Interceptors/SetReturnValueInterceptor.cs +++ b/src/Castle.Core.Tests/DynamicProxy.Tests/Interceptors/SetReturnValueInterceptor.cs @@ -24,7 +24,7 @@ public SetReturnValueInterceptor(object value) } - public void Intercept(IInvocation invocation) + public void Intercept(IInvocation invocation, InvocationDelegate proceed) { invocation.ReturnValue = value; } diff --git a/src/Castle.Core.Tests/DynamicProxy.Tests/Interceptors/ThrowingInterceptor.cs b/src/Castle.Core.Tests/DynamicProxy.Tests/Interceptors/ThrowingInterceptor.cs index ccdd4698d5..0485897d4a 100644 --- a/src/Castle.Core.Tests/DynamicProxy.Tests/Interceptors/ThrowingInterceptor.cs +++ b/src/Castle.Core.Tests/DynamicProxy.Tests/Interceptors/ThrowingInterceptor.cs @@ -16,7 +16,7 @@ namespace Castle.DynamicProxy.Tests.Interceptors { public class ThrowingInterceptor : IInterceptor { - public void Intercept(IInvocation invocation) + public void Intercept(IInvocation invocation, InvocationDelegate proceed) { throw new ThrowingInterceptorException("Because I feel like it"); } diff --git a/src/Castle.Core.Tests/DynamicProxy.Tests/Interceptors/WithCallbackInterceptor.cs b/src/Castle.Core.Tests/DynamicProxy.Tests/Interceptors/WithCallbackInterceptor.cs index 81b39d04f7..06704ba0e9 100644 --- a/src/Castle.Core.Tests/DynamicProxy.Tests/Interceptors/WithCallbackInterceptor.cs +++ b/src/Castle.Core.Tests/DynamicProxy.Tests/Interceptors/WithCallbackInterceptor.cs @@ -23,11 +23,11 @@ public WithCallbackInterceptor(InterceptorCallback interceptorCallback) callback = interceptorCallback; } - public void Intercept(IInvocation invocation) + public void Intercept(IInvocation invocation, InvocationDelegate proceed) { - callback(invocation); + callback(invocation, proceed); } - public delegate void InterceptorCallback(IInvocation invocation); + public delegate void InterceptorCallback(IInvocation invocation, InvocationDelegate proceed); } } \ No newline at end of file diff --git a/src/Castle.Core.Tests/DynamicProxy.Tests/InterceptorsMustReturnNonNullValueTypesTestCase.cs b/src/Castle.Core.Tests/DynamicProxy.Tests/InterceptorsMustReturnNonNullValueTypesTestCase.cs index 25f3c6b773..a1805f392a 100644 --- a/src/Castle.Core.Tests/DynamicProxy.Tests/InterceptorsMustReturnNonNullValueTypesTestCase.cs +++ b/src/Castle.Core.Tests/DynamicProxy.Tests/InterceptorsMustReturnNonNullValueTypesTestCase.cs @@ -92,11 +92,11 @@ public virtual int IntThrow() public class SwallowExceptionInterceptor : IInterceptor { - public void Intercept(IInvocation invocation) + public void Intercept(IInvocation invocation, InvocationDelegate proceed) { try { - invocation.Proceed(); + proceed(invocation); } catch (Exception) { @@ -107,9 +107,9 @@ public void Intercept(IInvocation invocation) public class ReturnNullValueInterceptor : IInterceptor { - public void Intercept(IInvocation invocation) + public void Intercept(IInvocation invocation, InvocationDelegate proceed) { - invocation.Proceed(); // If this throws, ReturnValue will remain null + proceed(invocation); // If this throws, ReturnValue will remain null invocation.ReturnValue = null; } } diff --git a/src/Castle.Core.Tests/DynamicProxy.Tests/InvocationMethodInvocationTargetTestCase.cs b/src/Castle.Core.Tests/DynamicProxy.Tests/InvocationMethodInvocationTargetTestCase.cs index 26b08b6938..aba358b413 100644 --- a/src/Castle.Core.Tests/DynamicProxy.Tests/InvocationMethodInvocationTargetTestCase.cs +++ b/src/Castle.Core.Tests/DynamicProxy.Tests/InvocationMethodInvocationTargetTestCase.cs @@ -101,16 +101,16 @@ public void InterfaceProxyWithTargetInterface_MethodInvocationTarget_should_be_u var proxy = generator.CreateInterfaceProxyWithTargetInterface( typeof(IService), target1, - new WithCallbackInterceptor(i => + new WithCallbackInterceptor((i, p) => { invocationTarget1 = i.MethodInvocationTarget; - i.Proceed(); + p(i); }), new ChangeTargetInterceptor(target2), - new WithCallbackInterceptor(i => + new WithCallbackInterceptor((i, p) => { invocationTarget2 = i.MethodInvocationTarget; - i.Proceed(); + p(i); })) as IService; proxy.Sum(2, 2); diff --git a/src/Castle.Core.Tests/DynamicProxy.Tests/MixinTestCase.cs b/src/Castle.Core.Tests/DynamicProxy.Tests/MixinTestCase.cs index cdd019888e..bb33d9c950 100644 --- a/src/Castle.Core.Tests/DynamicProxy.Tests/MixinTestCase.cs +++ b/src/Castle.Core.Tests/DynamicProxy.Tests/MixinTestCase.cs @@ -32,12 +32,12 @@ private class AssertInvocationInterceptor : StandardInterceptor public object proxy; public object mixin; - protected override void PreProceed(IInvocation invocation) + protected override void PreProceed(IInvocation invocation, InvocationDelegate proceed) { Invoked = true; mixin = invocation.InvocationTarget; proxy = invocation.Proxy; - base.PreProceed(invocation); + base.PreProceed(invocation, proceed); } } diff --git a/src/Castle.Core.Tests/DynamicProxy.Tests/OrderOfInterfacePrecedenceTestCase.cs b/src/Castle.Core.Tests/DynamicProxy.Tests/OrderOfInterfacePrecedenceTestCase.cs index 50f69178e4..866576d1f9 100644 --- a/src/Castle.Core.Tests/DynamicProxy.Tests/OrderOfInterfacePrecedenceTestCase.cs +++ b/src/Castle.Core.Tests/DynamicProxy.Tests/OrderOfInterfacePrecedenceTestCase.cs @@ -39,7 +39,7 @@ public void Same_Interface_on_target_and_mixin_should_forward_to_target() [Test] public void Same_Interface_on_proxy_withouth_target_and_mixin_should_forward_to_null_target() { - var interceptor = new WithCallbackInterceptor(i => + var interceptor = new WithCallbackInterceptor((i, p) => { Assert.IsNull(i.InvocationTarget); i.ReturnValue = 0; @@ -63,7 +63,7 @@ public void Same_Interface_on_target_of_proxy_with_target_interface_and_mixin_sh { var target = new ServiceImpl(); var mixin = new ServiceImpl(); - IInterceptor interceptor = new WithCallbackInterceptor(i=> + IInterceptor interceptor = new WithCallbackInterceptor((i, p)=> { Assert.AreSame(target,i.InvocationTarget); i.ReturnValue = 0; diff --git a/src/Castle.Core.Tests/DynamicProxy.Tests/OutRefParamsTestCase.cs b/src/Castle.Core.Tests/DynamicProxy.Tests/OutRefParamsTestCase.cs index b1ff5dc61d..f3c3a3dee0 100644 --- a/src/Castle.Core.Tests/DynamicProxy.Tests/OutRefParamsTestCase.cs +++ b/src/Castle.Core.Tests/DynamicProxy.Tests/OutRefParamsTestCase.cs @@ -52,11 +52,11 @@ public class ExceptionCatchInterceptor : StandardInterceptor { public Exception Exception { get; set; } - protected override void PerformProceed(IInvocation invocation) + protected override void PerformProceed(IInvocation invocation, InvocationDelegate proceed) { try { - base.PerformProceed(invocation); + base.PerformProceed(invocation, proceed); } catch (Exception e) { @@ -97,7 +97,7 @@ public void CanAffectValueOfOutParameter() { int i; var interceptor = - new WithCallbackInterceptor(delegate(IInvocation invocation) { invocation.Arguments[0] = 5; }); + new WithCallbackInterceptor(delegate(IInvocation invocation, InvocationDelegate proceed) { invocation.Arguments[0] = 5; }); var proxy = (IWithRefOut)generator.CreateInterfaceProxyWithoutTarget(typeof(IWithRefOut), interceptor); proxy.Do(out i); Assert.AreEqual(5, i); @@ -118,7 +118,7 @@ public void CanCreateComplexOutRefProxyOnClass() var i = 3; var s1 = "2"; string s2; - var interceptor = new WithCallbackInterceptor(delegate(IInvocation invocation) + var interceptor = new WithCallbackInterceptor(delegate(IInvocation invocation, InvocationDelegate proceed) { invocation.Arguments[0] = 5; invocation.Arguments[1] = "aaa"; @@ -144,7 +144,7 @@ public void CanCreateProxyWithRefParam() { var i = 3; var interceptor = - new WithCallbackInterceptor(delegate(IInvocation invocation) { invocation.Arguments[0] = 5; }); + new WithCallbackInterceptor(delegate(IInvocation invocation, InvocationDelegate proceed) { invocation.Arguments[0] = 5; }); var proxy = (IWithRefOut)generator.CreateInterfaceProxyWithoutTarget(typeof(IWithRefOut), interceptor); proxy.Did(ref i); Assert.AreEqual(5, i); diff --git a/src/Castle.Core.Tests/DynamicProxy.Tests/RhinoMocksTestCase.cs b/src/Castle.Core.Tests/DynamicProxy.Tests/RhinoMocksTestCase.cs index 68d14b18f8..8550672d00 100644 --- a/src/Castle.Core.Tests/DynamicProxy.Tests/RhinoMocksTestCase.cs +++ b/src/Castle.Core.Tests/DynamicProxy.Tests/RhinoMocksTestCase.cs @@ -37,7 +37,7 @@ public class SkipCallingMethodInterceptorWithOutputParams : IInterceptor { #region IInterceptor Members - public void Intercept(IInvocation invocation) + public void Intercept(IInvocation invocation, InvocationDelegate proceed) { invocation.Arguments[0] = IntPtr.Zero; invocation.ReturnValue = 5; @@ -392,7 +392,7 @@ public SetOutputParameter(decimal x) #region IInterceptor Members - public void Intercept(IInvocation invocation) + public void Intercept(IInvocation invocation, InvocationDelegate proceed) { invocation.Arguments[0] = x; } diff --git a/src/Castle.Core.Tests/DynamicProxy.Tests/ValueTypeReferenceSemanticsTestCase.cs b/src/Castle.Core.Tests/DynamicProxy.Tests/ValueTypeReferenceSemanticsTestCase.cs index 43d24559b7..4ee9be6531 100644 --- a/src/Castle.Core.Tests/DynamicProxy.Tests/ValueTypeReferenceSemanticsTestCase.cs +++ b/src/Castle.Core.Tests/DynamicProxy.Tests/ValueTypeReferenceSemanticsTestCase.cs @@ -53,7 +53,7 @@ public void Can_intercept_method_having_valuetypes_parameter_with_in_modifier() object receivedArg = null; var proxy = this.generator.CreateInterfaceProxyWithoutTarget( - new WithCallbackInterceptor(invocation => + new WithCallbackInterceptor((invocation, proceed) => { receivedArg = invocation.Arguments[0]; })); diff --git a/src/Castle.Core/DynamicProxy/AbstractInvocation.cs b/src/Castle.Core/DynamicProxy/AbstractInvocation.cs index d8d55adf65..d4eed410c6 100644 --- a/src/Castle.Core/DynamicProxy/AbstractInvocation.cs +++ b/src/Castle.Core/DynamicProxy/AbstractInvocation.cs @@ -16,6 +16,7 @@ namespace Castle.DynamicProxy { using System; using System.Diagnostics; + using System.Linq; using System.Reflection; public abstract class AbstractInvocation : IInvocation @@ -106,40 +107,14 @@ public void Proceed() return; } - currentInterceptorIndex++; - try - { - if (currentInterceptorIndex == interceptors.Length) - { - InvokeMethodOnTarget(); - } - else if (currentInterceptorIndex > interceptors.Length) - { - string interceptorsCount; - if (interceptors.Length > 1) - { - interceptorsCount = " each one of " + interceptors.Length + " interceptors"; - } - else - { - interceptorsCount = " interceptor"; - } - - var message = "This is a DynamicProxy2 error: invocation.Proceed() has been called more times than expected." + - "This usually signifies a bug in the calling code. Make sure that" + interceptorsCount + - " selected for the method '" + Method + "'" + - "calls invocation.Proceed() at most once."; - throw new InvalidOperationException(message); - } - else - { - interceptors[currentInterceptorIndex].Intercept(this); - } - } - finally + InvocationDelegate app = invocation => InvokeMethodOnTarget(); + + foreach (var interceptor in interceptors.AsEnumerable().Reverse()) { - currentInterceptorIndex--; + app = ((Func) (proceed => invocation => interceptor.Intercept(invocation, proceed)))(app); } + + app(this); } protected abstract void InvokeMethodOnTarget(); diff --git a/src/Castle.Core/DynamicProxy/IInterceptor.cs b/src/Castle.Core/DynamicProxy/IInterceptor.cs index 3a222524c1..e3ecdc0f05 100644 --- a/src/Castle.Core/DynamicProxy/IInterceptor.cs +++ b/src/Castle.Core/DynamicProxy/IInterceptor.cs @@ -19,6 +19,8 @@ namespace Castle.DynamicProxy /// public interface IInterceptor { - void Intercept(IInvocation invocation); + /// Invocation information. + /// Proceeds the call to the next interceptor in line, and ultimately to the target method. + void Intercept(IInvocation invocation, InvocationDelegate proceed); } } \ No newline at end of file diff --git a/src/Castle.Core/DynamicProxy/IInvocation.cs b/src/Castle.Core/DynamicProxy/IInvocation.cs index 35563d3235..43fb746bfe 100644 --- a/src/Castle.Core/DynamicProxy/IInvocation.cs +++ b/src/Castle.Core/DynamicProxy/IInvocation.cs @@ -104,16 +104,6 @@ public interface IInvocation /// MethodInfo GetConcreteMethodInvocationTarget(); - /// - /// Proceeds the call to the next interceptor in line, and ultimately to the target method. - /// - /// - /// Since interface proxies without a target don't have the target implementation to proceed to, - /// it is important, that the last interceptor does not call this method, otherwise a - /// will be thrown. - /// - void Proceed(); - /// /// Overrides the value of an argument at the given with the /// new provided. diff --git a/src/Castle.Core/DynamicProxy/InvocationDelegate.cs b/src/Castle.Core/DynamicProxy/InvocationDelegate.cs new file mode 100644 index 0000000000..2f45efaefa --- /dev/null +++ b/src/Castle.Core/DynamicProxy/InvocationDelegate.cs @@ -0,0 +1,4 @@ +namespace Castle.DynamicProxy +{ + public delegate void InvocationDelegate(IInvocation invocation); +} diff --git a/src/Castle.Core/DynamicProxy/StandardInterceptor.cs b/src/Castle.Core/DynamicProxy/StandardInterceptor.cs index ef0c9dcf3e..7c900336c3 100644 --- a/src/Castle.Core/DynamicProxy/StandardInterceptor.cs +++ b/src/Castle.Core/DynamicProxy/StandardInterceptor.cs @@ -25,23 +25,23 @@ public class StandardInterceptor : #endif IInterceptor { - public void Intercept(IInvocation invocation) + public void Intercept(IInvocation invocation, InvocationDelegate proceed) { - PreProceed(invocation); - PerformProceed(invocation); - PostProceed(invocation); + PreProceed(invocation, proceed); + PerformProceed(invocation, proceed); + PostProceed(invocation, proceed); } - protected virtual void PerformProceed(IInvocation invocation) + protected virtual void PerformProceed(IInvocation invocation, InvocationDelegate proceed) { - invocation.Proceed(); + proceed(invocation); } - protected virtual void PreProceed(IInvocation invocation) + protected virtual void PreProceed(IInvocation invocation, InvocationDelegate proceed) { } - protected virtual void PostProceed(IInvocation invocation) + protected virtual void PostProceed(IInvocation invocation, InvocationDelegate proceed) { } }