制作与异步方法Ninject拦截工作(Making Ninject Interceptors wor

2019-08-03 07:40发布

我开始与ninject拦截工作包一些与各种行为我异步代码和我有一些麻烦一切工作。

这是一个拦截器,我有工作:

public class MyInterceptor : IInterceptor
{
    public async void Intercept(IInvocation invocation)
    {
        try
        {
            invocation.Proceed();
            //check that method indeed returns Task
            await (Task) invocation.ReturnValue;
            RecordSuccess();
        }
        catch (Exception)
        {
            RecordError();
            invocation.ReturnValue = _defaultValue;
            throw;
        }
    }

这似乎在大多数正常情况下正常运行。 我不知道这是否会做我的期望。 虽然它似乎控制流返回给调用者异步,我还是有点担心的是,代理无意中阻塞线程或东西的可能性。

这且不说,我不能得到的异常处理工作。 对于这个测试案例:

[Test]
public void ExceptionThrown()
{
    try
    {
        var interceptor = new MyInterceptor(DefaultValue);
        var invocation = new Mock<IInvocation>();
        invocation.Setup(x => x.Proceed()).Throws<InvalidOperationException>();
        interceptor.Intercept(invocation.Object);
    }
    catch (Exception e)
    {

    }
}

我可以在catch块被击中的拦截器看到,但在我的测试中catch块永远不会从重新抛出的打击。 我比较困惑,因为没有代理或任何东西在这里,只是很简单的模拟考试和对象。 我还试图像Task.Run(() => interceptor.Intercept(invocation.Object)).Wait(); 在我的测试,仍然没有改变。 测试通过愉快,但NUnit的输出确实有异常消息。

我想我的东西搞乱了,我不太明白是怎么回事,因为我认为我做的一样多。 有没有更好的办法拦截异步方法? 我在做什么毛病关于异常处理?

Answer 1:

我建议你阅读我的async / await介绍 ,如果你还没有这样做。 你需要的是如何切实抓紧抓好async方法涉及到他们的返回Task ,以拦截下来。

考虑当前的Intercept实现。 作为svick评论说,这是最好的避免async void 。 原因之一是错误处理是不寻常的:从任何例外async void方法提高了对当前SynchronizationContext直接。

在你的情况下,如果Proceed方法会引发异常(如您的模拟意志),那么你的async void Intercept实施,将引发异常,这将获得直接发送到SynchronizationContext ( 这是默认-或线程池- SynchronizationContext因为这是单元测试 ,因为我在我的博客解释)。 所以,你会看到异常的一些随机的线程池线程提出,而不是在你的单元测试的情况下。

为了解决这个问题,你必须重新考虑Intercept 。 定期截取只允许你拦截的第一部分async方法; 到到的结果作出回应async方法,您将需要返回时响应Task完成。

下面是刚刚捕获返回一个简单的例子Task

public class MyInterceptor : IInterceptor
{
    public Task Result { get; private set; }

    public void Intercept(IInvocation invocation)
    {
        try
        {
            invocation.Proceed();
            Result = (Task)invocation.ReturnValue;
        }
        catch (Exception ex)
        {
            var tcs = new TaskCompletionSource<object>();
            tcs.SetException(ex);
            Result = tcs.Task;
            throw;
        }
    }
}

您还可能要运行NUnit的2.6.2或更高版本,增加了对支持async单元测试 。 这将使你await您的MyInterceptor.Result (这将适当提高在单元测试上下文除外)。

如果你想更复杂的异步拦截,你可以使用async -只是没有async void 。 ;)

// Assumes the method returns a plain Task
public class MyInterceptor : IInterceptor
{
    private static async Task InterceptAsync(Task originalTask)
    {
        // Await for the original task to complete
        await originalTask;

        // asynchronous post-execution
        await Task.Delay(100);
    }

    public void Intercept(IInvocation invocation)
    {
        // synchronous pre-execution can go here
        invocation.Proceed();
        invocation.ReturnValue = InterceptAsync((Task)invocation.ReturnValue);
    }
}

不幸的是,拦截必须继续同步,所以它不可能有异步执行前(除非你同步等待它完成,或使用IChangeProxyTarget )。 即使有这种限制,但是,你应该能够做几乎任何你需要使用上面的技术。



文章来源: Making Ninject Interceptors work with async methods