5

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

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

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

    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,我将无法使用返回类型的泛型(没有整个反射范围)。

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

4

1 回答 1

7

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

我不熟悉fluentAOP,但我已经async用Castle DynamicProxy进行了注入。

如果你想使用反射,你要做的是首先确定它是否是一个async调用(即,如果返回类型是一个子类或等于typeof(Task)。如果它是一个async调用,那么你将需要使用反射来拉出并将TTask<T>应用于您自己的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;
}
于 2013-03-04T13:56:43.490 回答