【问题标题】:return await Method.Invoke()返回等待 Method.Invoke()
【发布时间】:2013-02-18 09:59:48
【问题描述】:

我是 DRY 编码的忠实拥护者,我喜欢尽可能避免使用样板代码。因此,我将我所有的 WCF 通道 faff 重构为一个 AOP 类,它处理 WCF 通道的生命周期。

我也是 async-await 的忠实拥护者,尤其是 WCF,因为理论上它可以释放一个通常会休眠等待响应的线程。

所以我在fluentAOP库中创建了一个拦截器

    private static object InvokeOnChannel(IMethodInvocation methodInvocation)
    {
        var proxy = _factory.CreateChannel();
        var channel = (IChannel) proxy;
        try
        {
            channel.Open();
            var ret = methodInvocation.Method.Invoke(proxy, methodInvocation.Arguments);
            channel.Close();
            return ret;
        }
        catch (FaultException ex)
        {
            if (ex.InnerException != null)
                throw ex.InnerException;
            throw;
        }
        catch(Exception)
        {
            channel.Abort();
            throw;
        }
    }

但是,在考虑解决方案时,我注意到在 WCF 合同形式的情况下

[ServiceContract]
public interface IFoo
{
    [OperationContract]
    Task<int> GetInt();
}

GetInt 会产生意想不到的结果。首先,catch FaultException 什么都不做。其次,我会在请求返回之前关闭通道。如果返回类型是任务,我理论上可以切换到另一个代码路径。但我不知道如何等待 Task 的结果,然后返回可等待的结果。

这当然特别困难,因为使用运行时 AOP,我将无法使用返回类型的泛型(没有全部反射)。

任何想法如何将此函数实现为可等待对象,它会在完成时关闭通道并将异常捕获/编组到调用线程?

【问题讨论】:

    标签: c# aop async-await methodinfo


    【解决方案1】:

    要进行async 注入,您必须替换返回的任务。为了代码可读性,我建议将其替换为 async 方法,而不是使用 ContinueWith

    我不熟悉 fluentAOP,但我用 Castle DynamicProxy 完成了async 注入。

    如果要使用反射,首先要确定它是否是async 调用(即,返回类型是否为typeof(Task) 的子类或等于typeof(Task)。如果是@ 987654327@ 调用,那么您将需要使用反射将T 拉出Task&lt;T&gt; 并将其应用于您自己的async 方法:

    private static MethodInfo handleAsync = ...; // point this to HandleAsync<T>
    
    // Only called if the return type is Task/Task<T>
    private static object InvokeAsyncOnChannel(IMethodInvocation methodInvocation)
    {
        var proxy = _factory.CreateChannel();
        var channel = (IChannel) proxy;
        try
        {
            channel.Open();
            var task = methodInvocation.Method.Invoke(proxy, methodInvocation.Arguments) as Task;
            object ret;
            if (task.GetType() == typeof(Task))
                ret = HandleAsync(task, channel);
            else
                ret = handleAsync.MakeGenericMethod(task.GetType().GetGenericParameters()).Invoke(this, task, channel);
            return ret;
        }
        catch (FaultException ex)
        {
            if (ex.InnerException != null)
                throw ex.InnerException;
            throw;
        }
        catch(Exception)
        {
            channel.Abort();
            throw;
        }
    }
    
    private static async Task HandleAsync(Task task, IChannel channel)
    {
        try
        {
            await task;
            channel.Close();
        }
        catch (FaultException ex)
        {
            if (ex.InnerException != null)
                throw ex.InnerException;
            throw;
        }
        catch(Exception)
        {
            channel.Abort();
            throw;
        }
    }
    
    private static async Task<T> HandleAsync<T>(Task task, IChannel channel)
    {
        try
        {
            var ret = await (Task<T>)task;
            channel.Close();
            return ret;
        }
        catch (FaultException ex)
        {
            if (ex.InnerException != null)
                throw ex.InnerException;
            throw;
        }
        catch(Exception)
        {
            channel.Abort();
            throw;
        }
    }
    

    另一种方法是使用dynamic

    private static object InvokeOnChannel(IMethodInvocation methodInvocation)
    {
        var proxy = _factory.CreateChannel();
        var channel = (IChannel) proxy;
        try
        {
            channel.Open();
            dynamic result = methodInvocation.Method.Invoke(proxy, methodInvocation.Arguments);
            return Handle(result, channel);
        }
        catch (FaultException ex)
        {
            if (ex.InnerException != null)
                throw ex.InnerException;
            throw;
        }
        catch(Exception)
        {
            channel.Abort();
            throw;
        }
    }
    
    private static async Task Handle(Task task, IChannel channel)
    {
        try
        {
            await task;
            channel.Close();
        }
        catch (FaultException ex)
        {
            if (ex.InnerException != null)
                throw ex.InnerException;
            throw;
        }
        catch(Exception)
        {
            channel.Abort();
            throw;
        }
    }
    
    private static async Task<T> Handle<T>(Task<T> task, IChannel channel)
    {
        await Handle((Task)task, channel);
        return await task;
    }
    
    private static T Handle<T>(T result, IChannel channel)
    {
        channel.Close();
        return result;
    }
    

    【讨论】:

    • 该死的。和我想的一样。必须使用通用的 get 方法。
    • 我正在玩dynamic 函数,看看我是否可以进一步简化它,但到目前为止看起来并不乐观。
    • 不是动态的忠实粉丝,只是因为您必须引用 CSharp 库。无论如何,看起来你的代码是最好的……希望 C# 对元编程更好一点。
    • 是的,我再简单不过了。我完全同意重新元编程!
    猜你喜欢
    • 2014-06-30
    • 2020-03-13
    • 1970-01-01
    • 1970-01-01
    • 2014-10-06
    • 2021-04-30
    • 2013-05-13
    • 2020-02-15
    • 2021-01-01
    相关资源
    最近更新 更多