我尝试从Controller发送电子邮件异步并收到以下错误:
我没有想要等待发送电子邮件以完成操作。
异步模块或处理程序在异步时完成 手术还在等待。
这是我的实施:我也尝试使用void
代替async Task
public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
{
var user = await _userManager.FindByNameAsync(model.Email);
if (user != null)
{
if (!await _userManager.IsEmailConfirmedAsync(user.Id))
{
//dont want to await result
//just send email
_mailService.SendAccountConfirmationEmailAsync(user);
...
}
...
}
return View();
}
public async Task SendAccountConfirmationEmailAsync(ApplicationUser user)
{
dynamic email = GetAccountConfirmationEmailTemplate();
email.Username = user.Email;
email.Name = user.UserName;
email.To = user.Email;
email.AccountConfirmationUrl = await GetAccountConfirmationUrl(user);
await _mailService.SendAsync(email);
}
我错过了什么?
答案 0 :(得分:5)
由于运行时可以在知道没有更多待处理请求的情况下回收您的应用程序域,因此特别不建议启动您不等待的任务,因此该消息。
特别是,如果没有此检查,您将无法保证此电子邮件将被发送,因为在您的控制器操作返回后,整个应用程序域可能会被围绕该任务拉下来。
现在在您的情况下,您可能会说丢失电子邮件是可以接受的,但运行时并不知道它只是电子邮件,它很可能是您的数据库调用您已经<忘记等待的事情会过早中止。因此,它只是告诉你这是不好的形式。
相反,你有两个选择:
最后一部分是HostingEnvironment.QueueBackgroundWorkItem的用途。
在你的情况下你会这样称呼:
HostingEnvironment.QueueBackgroundWorkItem(ct =>
_mailService.SendAccountConfirmationEmailAsync(user));
此外,该方法提供了CancellationToken
,您应该检查是否存在接受它的SendAccountConfirmationEmailAsync
重载。如果没有,如果这是您的API,则应考虑添加对此类令牌的支持。
请注意,此机制不适用于长时间运行的线程或任务,因为还有其他更合适的树可以树立,但在这种情况下它应该足够了。
答案 1 :(得分:2)
您需要等待异步调用以避免出现错误消息,但是在您的操作方法返回之前,您需要等待电子邮件完成发送(并且发送电子邮件失败将导致您的操作错误的方法)。
await _mailService.SendAccountConfirmationEmailAsync(user);
如果您看到以-Async结尾的方法名称,请检查该方法是否返回任务或任务。如果确实如此,则需要等待它。
如果你有一个异步通话,你不想在这种情况下等待它完成,你就会得到那个错误。如果要继续处理,您需要一些在后台任务中发送此电子邮件的方法。这可能是您有一个单独的应用程序来处理电子邮件,并通过像RabbitMQ这样的消息传递系统与它进行通信。或者有ways to run code in the background directly in a website,例如Hangfire。