返回等待 Method.Invoke() 的结果。

5
我是一个DRY编码的铁粉,尽可能避免样板代码。因此,我将所有的WCF通道烦恼重构成了一个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可能会产生意外结果。首先,捕获FaultException将不起作用。其次,在请求返回之前,我将关闭通道。如果返回类型为Task,则理论上可以切换到另一条代码路径。但我不知道如何等待Task<>的结果,然后返回一个awaitable。

当然,这特别困难,因为在运行时AOP中,我将无法使用返回类型的泛型(需要使用反射等诡异方法)。

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

1个回答

7
要进行异步注入,您需要替换返回的任务。为了提高代码可读性,我建议将其替换为一个异步方法,而不是使用ContinueWith。
我对fluentAOP不熟悉,但我已经在Castle DynamicProxy中完成了异步注入。
如果您想使用反射,您需要首先确定它是否是一个异步调用(例如,如果返回类型是Task的子类或等于typeof(Task))。如果它是一个异步调用,那么您将需要使用反射从Task 中提取T并将其应用到自己的异步方法中:
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;
}

该死。正如我所料。必须使用通用的获取方法。 - Aron
我正在尝试使用“动态”函数来简化这个问题,但目前看起来并不乐观。 - Stephen Cleary
不太喜欢动态语言,因为你必须引用CSharp库。无论如何,看起来你的代码是最好的...有点希望C#在元编程方面能更好一些。 - Aron
是的,我没有更简单的方法了。而且我完全同意元编程! - Stephen Cleary

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接