异步ActionMethod - 这是正确的

时间:2013-01-02 06:13:13

标签: asp.net asp.net-mvc-4

仅仅因为有效的东西并不意味着它是正确的。所以我想对以下代码提供一些反馈。

有点历史;当用户在我们的网站上注册并遇到线程被阻止的问题时,我试图发送/排队电子邮件,这完全有意义,因为默认情况下控制器和相关的操作方法是同步而不是异步。为了解决这个问题,我将以下内容汇总在一起,但不确定这是否是最佳方式。

 [HttpPost, AllowAnonymous]
    public async Task<ActionResult> RegisterAsync(UserRegisterUserViewModel userRegisterUserViewModel)
    {
        if (ModelState.IsValid)
        {
            var user = new UserDto
                {
                    UserName = userRegisterUserViewModel.UserName,
                    Password = userRegisterUserViewModel.Password,
                    AuthType = userRegisterUserViewModel.AuthType,
                    Active = 0
                };
            Guid userId = _userService.AddUser(user);
            if (userId != Guid.Empty)
            {
                // Send Registration E-mail
                await Task.Run(() => _userMailer.RegistrationConfirmation(user).SendAsync(),
                               new CancellationToken(false));
                // Display Confirm View
                return PartialView("_RegistrationConfirmation");
            }
            ModelState.AddModelError("UserName", "Unable to create account");
        }
        return PartialView("_Registration");
    }

1 个答案:

答案 0 :(得分:2)

我不确定ASP.NET线程被阻止会导致什么样的“问题”,但是关于async / await代码:

  • new CancellationToken(false)中没有任何意义,与CancellationToken.None相同,这与仅提供cancellationToken参数相同。
  • Task.Run可能没有意义,它在线程池线程上执行SendAsync。如果SendAsync是正确的async方法,那么它可以直接await
例如,该行代码可以替换为:

await _userMailer.RegistrationConfirmation(user).SendAsync();

修改

由于SendAsync位于SmtpClient,您应该wrap this API(使用Event-based Asynchronous Pattern)加入await友好的API(使用Task-based Asynchronous Pattern }):

public static Task SendTaskAsync(this SmtpClient client, MailMessage message)
{
  var tcs = new TaskCompletionSource<object>();
  SendCompletedEventHandler handler = null;
  handler = (s,e) =>
  {
    client.SendCompleted -= handler;
    if (e.Error != null) tcs.TrySetException(e.Error);
    else if (e.Cancelled) tcs.TrySetCanceled();
    else tcs.TrySetResult(null);
  };
  client.SendCompleted += handler;
  client.SendAsync(message, null);
  return tcs.Task;
}

然后您可以await SmtpClient.SendTaskAsync的结果。

您不想使用Task.Run,因为这会导致您return early from ASP.NET requests, which is a dangerous practice(正如我在博客中解释的那样)。