Last active
January 10, 2021 22:06
-
-
Save stakx/26fd79d5c59f90067e439bf2927e8982 to your computer and use it in GitHub Desktop.
AsyncInterceptor for Castle DynamicProxy, assuming that IInvocation.GetProceedInfo() is available. Doesn't yet support exceptions.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public abstract class AsyncInterceptor : IInterceptor | |
{ | |
private static readonly MethodInfo transitionMethod = | |
typeof(AsyncInterceptor).GetMethod(nameof(TransitionToAsync), BindingFlags.NonPublic | BindingFlags.Instance); | |
protected AsyncInterceptor() | |
{ | |
} | |
void IInterceptor.Intercept(IInvocation invocation) | |
{ | |
var returnType = invocation.Method.ReturnType; | |
if (returnType == typeof(Task)) | |
{ | |
invocation.ReturnValue = this.InterceptAsync(new AsyncInvocation(invocation, false)); | |
} | |
else if (returnType.BaseType == typeof(Task)) | |
{ | |
Debug.Assert(returnType.IsConstructedGenericType && returnType.GetGenericTypeDefinition() == typeof(Task<>)); | |
var resultType = returnType.GetGenericArguments()[0]; | |
invocation.ReturnValue = transitionMethod.MakeGenericMethod(resultType).Invoke(this, new object[] { invocation }); | |
} | |
else | |
{ | |
this.Intercept(invocation); | |
} | |
} | |
private async Task<T> TransitionToAsync<T>(IInvocation invocation) | |
{ | |
var asyncInvocation = new AsyncInvocation(invocation, true); | |
await this.InterceptAsync(asyncInvocation); | |
return (T)asyncInvocation.Result; | |
} | |
protected abstract void Intercept(IInvocation invocation); | |
protected abstract Task InterceptAsync(AsyncInvocation invocation); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public sealed class AsyncInvocation | |
{ | |
private readonly IInvocation invocation; | |
private readonly IInvocationProceedInfo proceed; | |
private object result; | |
private readonly bool mustReturnValue; | |
internal AsyncInvocation(IInvocation invocation, bool mustReturnValue) | |
{ | |
this.invocation = invocation; | |
this.proceed = invocation.CaptureProceedInfo(); | |
this.mustReturnValue = mustReturnValue; | |
} | |
public object[] Arguments => this.invocation.Arguments; | |
public MethodInfo Method => this.invocation.Method; | |
public object Result | |
{ | |
get => this.result; | |
set | |
{ | |
if (!this.mustReturnValue) | |
{ | |
throw new InvalidOperationException(); | |
} | |
else | |
{ | |
this.result = value; | |
} | |
} | |
} | |
public async Task ProceedAsync() | |
{ | |
var previousReturnValue = this.invocation.ReturnValue; | |
try | |
{ | |
this.proceed.Invoke(); | |
var returnValue = this.invocation.ReturnValue; | |
if (returnValue is Task task) | |
{ | |
await task; | |
if (this.mustReturnValue) | |
{ | |
var returnType = returnValue.GetType(); | |
Debug.Assert(returnType.IsConstructedGenericType && returnType.GetGenericTypeDefinition() == typeof(Task<>)); | |
var result = returnType.GetProperty("Result").GetValue(returnValue); | |
this.Result = result; | |
} | |
} | |
} | |
finally | |
{ | |
this.invocation.ReturnValue = previousReturnValue; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
IInvocationProceedInfo
has since become available and I have published stakx/DynamicProxy.AsyncInterceptor on NuGet: stakx.DynamicProxy.AsyncInterceptor.