首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >截取通过DynamicProxy返回泛型Task<>的异步方法

截取通过DynamicProxy返回泛型Task<>的异步方法
EN

Stack Overflow用户
提问于 2015-01-23 06:15:18
回答 5查看 7.9K关注 0票数 28

我的问题与这篇文章有关,Intercept the call to an async method using DynamicProxy

我想实现拦截器,它与返回TaskTask<T>结果的异步方法一起工作。

我使用next代码返回ContinueWith结果(为了让调用者方法在拦截器完成工作时等待)

代码语言:javascript
复制
var task = invocation.ReturnValue as Task;
invocation.ReturnValue = task.ContinueWith(c => 
      { code that should execute after method finish });

上面的代码可以很好地处理Task结果,但是如果是Task<T>结果,ContinueWith会将返回类型从Task<T>改为Task。我需要调用返回Task<T>的重载方法ContinueWith,但为此我需要将invocation.ReturnValue强制转换为Task<T>

我没有找到以任何方式动态转换的方法。有人知道怎么做吗?

我也尝试过通过反射调用这个方法,但是参数是labmda函数,不能直接传递。

EN

回答 5

Stack Overflow用户

回答已采纳

发布于 2015-02-07 04:23:02

经过广泛的研究,我能够创建一个解决方案,用于截取同步方法以及异步任务和异步Task< TResult >。

下面是我的异常处理拦截器的代码,它使用Castle动态代理处理所有这些方法类型。此模式适用于执行您希望的任何类型的拦截。对于标准的BeforeInvoke/AfterInvoke操作,语法会更清晰一些,但概念应该是相同的。

(其他注意事项:示例中的IExceptionHandler接口是自定义类型,而不是通用对象。)

代码语言:javascript
复制
    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
        }
    }
票数 22
EN

Stack Overflow用户

发布于 2016-09-30 13:51:02

更好的解决方案是使用dynamic关键字绕过编译器类型检查,并在运行时解析操作:

代码语言:javascript
复制
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;
}
票数 15
EN

Stack Overflow用户

发布于 2017-04-07 16:09:38

由于需要拦截返回Task<TResult>的方法,我创建了一个Castle.Core扩展来简化这个过程。

Castle.Core.AsyncInterceptor

该程序包可从NuGet下载。

该解决方案在很大程度上基于@silas-reinagel的回答,但通过提供实现IAsyncInterceptor的新接口简化了该解决方案。还有更多的抽象,使得拦截类似于实现Interceptor

有关更多详细信息,请参阅项目的readme

票数 5
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/28099669

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档