这是因为async
方法跟踪它们的完成情况,甚至async void
。他们通过注册来做到这一点SynchronizationContext
启动时并在返回时标记操作完成。 ASP.NET 跟踪所有创建的操作,并要求它们在您的操作返回之前全部完成,否则它将向 HTTP 客户端返回错误。如果您需要运行“即发即忘”方法,则必须手动避免在 ASP.NET 上启动它SynchronizationContext
。例如,await Task.Run(() => CallFireAndForget())
(await
这样您就可以等待同步部分在线程池上运行。这有效是因为CallFireAndForget()
is async void
。触发一个返回 a 的方法Task
作为一劳永逸的方式,你必须明确避免返回Task
to Task.Run()
像这样:await Task.Run(() => { CallFireAndForgetAsync(); })
).
显示相同错误消息的另一种方法应该是将其写入异步操作中:
// BAD CODE, DEMONSTRATION ONLY!
AsyncOperationManager.CreateOperation();
如果您使用AsyncOperation
API,你必须确保你调用AsyncOperation.OperationCompleted()
在允许操作方法返回之前。
就您而言,如果邮件发送确实是一项即发即忘的任务,您可以在CreateUser
将其签名更改为后async Task CreateUserAsync(ViewModel.VM_User user)
:
await Task.Run(() => Email("CreateUser", user).DeliverAsync());
并在你的行动中:
await new MailController().CreateUserAsync(user.userDetails);
理想情况下,你不会使用Task.Run()
对于这样的事情。相反,您可以暂时替换当前的SynchronizationContext
反而 (请注意:从不await
在 - 的里面try{}
- 相反,如果您需要,请存储Task
在本地并恢复原始后等待它SynchronizationContext
(使用原语可能更安全,例如SynchronizationContextSwitcher.NoContext() (source) 做正确的事)):
var originalSynchronizationContext = SynchronizationContext.Current;
try
{
SynchronizationContext.SetSynchronizationContext(null);
new MailController().CreateUser(user.userDetails);
}
finally
{
SynchronizationContext.SetSynchronizationContext(originalSynchronizationContext);
}