仅仅因为有效的东西并不意味着它是正确的。所以我想对以下代码提供一些反馈。
有点历史;当用户在我们的网站上注册并遇到线程被阻止的问题时,我试图发送/排队电子邮件,这完全有意义,因为默认情况下控制器和相关的操作方法是同步而不是异步。为了解决这个问题,我将以下内容汇总在一起,但不确定这是否是最佳方式。
[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");
}
答案 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(正如我在博客中解释的那样)。