我开始与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的输出确实有异常消息。
我想我的东西搞乱了,我不太明白是怎么回事,因为我认为我做的一样多。 有没有更好的办法拦截异步方法? 我在做什么毛病关于异常处理?
我建议你阅读我的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
)。 即使有这种限制,但是,你应该能够做几乎任何你需要使用上面的技术。