gpt4 book ai didi

c# - 使 Ninject 拦截器与异步方法一起工作

转载 作者:太空狗 更新时间:2023-10-29 22:27:17 25 4
gpt4 key购买 nike

我开始使用 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 block 被命中,但我的测试中的 catch block 从未被重新抛出命中。我更困惑,因为这里没有代理或任何东西,只有非常简单的模拟和对象。我还在测试中尝试了 Task.Run(() => interceptor.Intercept(invocation.Object)).Wait(); 之类的东西,但仍然没有变化。测试顺利通过,但 nUnit 输出确实有异常信息。

我想我把事情搞砸了,而且我并不像我认为的那样完全理解正在发生的事情。有没有更好的方法来拦截异步方法?关于异常处理,我做错了什么?

最佳答案

我建议您阅读我的 async/await intro ,如果您还没有这样做的话。您需要很好地掌握 async 方法如何与其返回的 Task 相关,以便拦截它们。

考虑您当前的 Intercept 实现。正如 svick 评论的那样,最好避免 async void。原因之一是错误处理不寻常:async void 方法的任何异常都会直接在当前 SynchronizationContext 上引发。

在您的情况下,如果 Proceed 方法引发异常(就像您的 mock 那样),那么您的 async void Intercept 实现将引发异常,该异常将被发送直接到 SynchronizationContext(which is a default - or thread pool - SynchronizationContext since this is a unit test,正如我在我的博客中解释的那样)。因此,您会看到该异常是在某个随机线程池线程上引发的,而不是在单元测试的上下文中引发的。

要解决此问题,您必须重新考虑 Intercept。常规拦截只允许您拦截 async 方法的 first 部分;要响应 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 or later, which added support for async unit tests .这将使您能够等待您的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)。不过,即使有这个限制,您也应该能够使用上述技术做几乎任何您需要做的事情。

关于c# - 使 Ninject 拦截器与异步方法一起工作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13630548/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com