return await Method.Invoke()

Aro*_*ron 5 c# aop methodinfo async-await

我是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;
        }
    }
Run Code Online (Sandbox Code Playgroud)

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

[ServiceContract]
public interface IFoo
{
    [OperationContract]
    Task<int> GetInt();
}
Run Code Online (Sandbox Code Playgroud)

GetInt会有意想不到的结果.首先,catchException将无效.其次,我会在请求返回之前关闭通道.如果返回类型是Task,我理论上可以切换到另一个代码路径.但我无法弄清楚如何等待任务<>的结果然后返回等待.

这当然是特别困难的,因为运行时AOP我无法访问能够使用返回类型的泛型(没有整个反射).

任何想法如何实现这个功能作为一个等待,它关闭完整的通道和捕获/编组调用线程的异常?

Ste*_*ary 7

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

我不熟悉fluentAOP,但我已经完成async了Castle DynamicProxy的注入.

如果你想使用反射,你要做的是首先确定它是否是一个async调用(即,如果返回类型是一个子类或等于typeof(Task).如果它是一个async调用,那么你将需要使用反射拉T出来Task<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;
    }
}
Run Code Online (Sandbox Code Playgroud)

另一种方法是使用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;
}
Run Code Online (Sandbox Code Playgroud)