发送电子邮件异步:在异步操作仍处于挂起状态时完成异步模块或处理程序

m.r*_*fca 1 c# asp.net-mvc async-await

我正在尝试从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();
}
Run Code Online (Sandbox Code Playgroud)

.

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);
}
Run Code Online (Sandbox Code Playgroud)

我错过了什么?

ang*_*son 5

由于运行时可以在知道没有更多待处理请求时回收您的应用程序域,因此特别不推荐您不等待的启动任务,因此该消息.

特别是,如果没有此检查,您将无法保证此电子邮件将被发送,因为在您的控制器操作返回后,整个应用程序域可能会被围绕该任务拉下来.

现在在你的情况下,你可能会说丢失电子邮件是可以接受的,但是运行时不知道它只是一封电子邮件,很可能是你忘记等待的数据库调用过早了中止.因此,它只是告诉你这是不好的形式.

相反,你有两个选择:

  1. 等待该任务完成,您已经明确表示您不想这样做
  2. 通知运行时你正在解雇一个你特别不想等待的任务,但请哦,请不要在任务完成之前拉出appdomain

最后一部分是HostingEnvironment.QueueBackgroundWorkItem的用途.

在你的情况下你会这样称呼它:

HostingEnvironment.QueueBackgroundWorkItem(ct =>
    _mailService.SendAccountConfirmationEmailAsync(user));
Run Code Online (Sandbox Code Playgroud)

另外,该方法提供了一个CancellationToken,您应该检查是否存在SendAccountConfirmationEmailAsync接受它的重载.如果没有,如果这是您的API,您应该考虑添加对此类令牌的支持.

请注意,此机制不适用于长时间运行的线程或任务,因为还有其他更合适的树可以树立,但在这种情况下它应该足够了.

  • 请注意,"HostingEnvironment"是[在ASP.NET Core上不可用](https://docs.microsoft.com/en-us/dotnet/api/system.web.hosting.hostingenvironment.queuebackgroundworkitem?view=netcore- 2.0).你需要实现自己的东西[使用`IApplicationLifetime`](/sf/ask/1937329831/# 27714453) (4认同)