tareas paralelas net mvc metodos hilos hebras ejecutar ejecucion delegados async asp asincronos asincrona c# generics async-await castle-dynamicproxy

c# - paralelas - Intercepte un método asíncrono que devuelve la Tarea genérica<> a través de DynamicProxy



metodos async c# mvc (4)

Al tener la necesidad de interceptar métodos que devuelven la Task<TResult> , he creado una extensión para Castle.Core que simplifica el proceso.

Castle.Core.AsyncInterceptor

El paquete está disponible para descargar en NuGet .

La solución se basa en gran medida en la respuesta de , pero la simplifica al proporcionar una nueva interfaz para implementar IAsyncInterceptor . También hay abstracciones adicionales que hacen que la intercepción sea similar a la implementación de Interceptor .

Ver el readme del proyecto para más detalles.

Mis preguntas están relacionadas con esta publicación. Intercepte la llamada a un método asíncrono utilizando DynamicProxy.

Quiero implementar un interceptor que funcione con métodos asíncronos que devuelven Task resultado Task o Task<T> .

Uso el siguiente código para devolver el resultado de ContinueWith (para que el método del llamante espere mientras el interceptor termina de funcionar)

var task = invocation.ReturnValue as Task; invocation.ReturnValue = task.ContinueWith(c => { code that should execute after method finish });

El código anterior funciona bien para Task resultado de la Task , pero en el caso de la Task<T> resultado ContinueWith cambiará el tipo de retorno de la Task<T> a la Task . Necesito llamar al método sobrecargado Continuar con el que devuelve la Task<T> , pero para esto necesito convertir la invocation.ReturnValue a la Task<T>

No encontré la forma de lanzarlo dinámicamente de ninguna manera. ¿Alguien sabe como hacerlo?

También intenté llamar a este método a través de la reflexión, pero el parámetro es la función labmda que no se puede pasar directamente.


Después de una extensa investigación, pude crear una solución que funciona para interceptar los métodos síncronos, así como la tarea asíncrona y la tarea asíncrona <TResult>.

Aquí está mi código para un interceptor de manejo de excepciones que funciona en todos esos tipos de métodos, usando Castle Dynamic Proxy. Este patrón es adaptable para hacer cualquier tipo de intercepción que desee. La sintaxis será un poco más limpia para las acciones estándar de BeforeInvoke / AfterInvoke, pero el concepto debería ser el mismo.

(Otra nota: la interfaz IExceptionHandler en el ejemplo es un tipo personalizado y no un objeto común).

private class AsyncExceptionHandlingInterceptor : IInterceptor { private static readonly MethodInfo handleAsyncMethodInfo = typeof(AsyncExceptionHandlingInterceptor).GetMethod("HandleAsyncWithResult", BindingFlags.Instance | BindingFlags.NonPublic); private readonly IExceptionHandler _handler; public AsyncExceptionHandlingInterceptor(IExceptionHandler handler) { _handler = handler; } public void Intercept(IInvocation invocation) { var delegateType = GetDelegateType(invocation); if (delegateType == MethodType.Synchronous) { _handler.HandleExceptions(() => invocation.Proceed()); } if (delegateType == MethodType.AsyncAction) { invocation.Proceed(); invocation.ReturnValue = HandleAsync((Task)invocation.ReturnValue); } if (delegateType == MethodType.AsyncFunction) { invocation.Proceed(); ExecuteHandleAsyncWithResultUsingReflection(invocation); } } private void ExecuteHandleAsyncWithResultUsingReflection(IInvocation invocation) { var resultType = invocation.Method.ReturnType.GetGenericArguments()[0]; var mi = handleAsyncMethodInfo.MakeGenericMethod(resultType); invocation.ReturnValue = mi.Invoke(this, new[] { invocation.ReturnValue }); } private async Task HandleAsync(Task task) { await _handler.HandleExceptions(async () => await task); } private async Task<T> HandleAsyncWithResult<T>(Task<T> task) { return await _handler.HandleExceptions(async () => await task); } private MethodType GetDelegateType(IInvocation invocation) { var returnType = invocation.Method.ReturnType; if (returnType == typeof(Task)) return MethodType.AsyncAction; if (returnType.IsGenericType && returnType.GetGenericTypeDefinition() == typeof(Task<>)) return MethodType.AsyncFunction; return MethodType.Synchronous; } private enum MethodType { Synchronous, AsyncAction, AsyncFunction } }


He hecho de esta manera:

invocation.Proceed(); object response; Type type = invocation.ReturnValue?.GetType(); if (type != null && typeof(Task).IsAssignableFrom(type)) { var resultProperty = type.GetProperty("Result"); response = resultProperty.GetValue(invocation.ReturnValue); }


Una mejor solución sería utilizar la palabra clave dynamic para omitir la comprobación de tipo de compilador y resolver la operación en tiempo de ejecución:

public void Intercept(IInvocation invocation) { invocation.Proceed(); var method = invocation.MethodInvocationTarget; var isAsync = method.GetCustomAttribute(typeof(AsyncStateMachineAttribute)) != null; if (isAsync && typeof(Task).IsAssignableFrom(method.ReturnType)) { invocation.ReturnValue = InterceptAsync((dynamic)invocation.ReturnValue); } } private static async Task InterceptAsync(Task task) { await task.ConfigureAwait(false); // do the continuation work for Task... } private static async Task<T> InterceptAsync<T>(Task<T> task) { T result = await task.ConfigureAwait(false); // do the continuation work for Task<T>... return result; }