c# - 返回 await Method.Invoke()

标签 c# aop async-await methodinfo

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

我也是 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 契约(Contract)的情况下

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

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

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

关于如何将此函数实现为可等待对象的任何想法,它会在完成时关闭 channel 并捕获/编码(marshal)调用线程的异常?

最佳答案

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

我对 fluentAOP 不熟悉,但是我用 CaSTLe DynamicProxy 完成了 async 注入(inject)。

如果你想使用反射,你要做的是首先确定它是否是一个 async 调用(即,如果返回类型是 typeof(Task) 的子类或等于 async 。如果它是一个 T 调用,那么你将需要使用反射从 Task<T> 中提取 async 并将其应用于您自己的 dynamic 方法:

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;
    }
}

另一种方法是使用 ojit_code :

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;
}

关于c# - 返回 await Method.Invoke(),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15199447/

相关文章:

java - 如何在构造函数上设置切面

spring - 使用MyBatis时如何自动更新创建/修改日期等字段?

c# - 转换/包装一个使用回调的 "classic"异步方法

c# - 等待 IAsyncAction 时应该使用 ConfigureAwait(false) 吗?

c# - 将字节数组转换为不带 FromStream 的图像(对于 Mono)

c# - azure 上的 FFMpeg 函数无法处理同时调用(超过 5 个)

java - 使用 AOP 拦截私有(private)注释方法

javascript - 函数不返回 pg-pool 结果

c# - 将复杂类型作为参数传递给 ASMX 服务

c# - 网页浏览器执行流程