我尝试在asp.net identity 2.0应用程序中发送验证电子邮件地址电子邮件,但收到TaskCanceledException错误。
我在这个主题中试过了什么:Asp.Net Identity 2.0 - How to Implement IIdentityMessageService to do Async SMTP using SmtpClient?
如果我使用await
应用程序只是永远运行:没有错误消息。但第二个选项会返回错误。
错误发生在:return smtpClient.SendMailAsync(msg);
IdentityConfig.cs
public async Task SendAsync(IdentityMessage message)
{
// Plug in your email service here to send an email.
try
{
#region formatter
string text = string.Format("Please click on this link to {0}: {1}", message.Subject, message.Body);
string html = "Please confirm your account by clicking this link: <a href=\"" + message.Body + "\">link</a><br/>";
html += HttpUtility.HtmlEncode(@"Or click on the copy the following link on the browser:" + message.Body);
#endregion
MailMessage msg = new MailMessage();
msg.From = new MailAddress("");
msg.To.Add(new MailAddress(message.Destination));
msg.Subject = message.Subject;
msg.AlternateViews.Add(AlternateView.CreateAlternateViewFromString(text, null, MediaTypeNames.Text.Plain));
msg.AlternateViews.Add(AlternateView.CreateAlternateViewFromString(html, null, MediaTypeNames.Text.Html));
using (var smtpClient = new SmtpClient())
{
smtpClient.EnableSsl = true;
smtpClient.SendCompleted += (s, e) => { smtpClient.Dispose(); };
await smtpClient.SendMailAsync(msg);
}
}
catch (Exception ex)
{
throw ex;
}
}
Register.aspx.cs
protected void CreateUser_Click(object sender, EventArgs e)
{
var manager = Context.GetOwinContext().GetUserManager<ApplicationUserManager>();
var user = new ApplicationUser()
{
UserName = Email.Text,
Email = Email.Text,
UserProfileInfo = new UserProfileInfo
{
FirstName = Firstname.Text,
LastName = Lastname.Text,
Adress = Adress.Text,
Zip = Zip.Text,
City = City.Text,
Mobile = Mobile.Text
}
};
IdentityResult result = manager.Create(user, Password.Text);
if (result.Succeeded)
{
// For more information on how to enable account confirmation and password reset please visit http://go.microsoft.com/fwlink/?LinkID=320771
string code = manager.GenerateEmailConfirmationToken(user.Id);
string callbackUrl = IdentityHelper.GetUserConfirmationRedirectUrl(code, user.Id, Request);
manager.SendEmail(user.Id, "Confirm your account", "Please confirm your account by clicking <a href=\"" + callbackUrl + "\">here</a>.");
manager.AddToRole(user.Id, "Konsument");
IdentityHelper.SignIn(manager, user, isPersistent: false);
IdentityHelper.RedirectToReturnUrl(Request.QueryString["ReturnUrl"], Response);
}
else
{
ErrorMessage.Text = result.Errors.FirstOrDefault();
}
}
答案 0 :(得分:3)
@ntl正确识别了问题:SmtpClient
实例在请求完成之前正在处理。不过,我更喜欢使用async
和await
作为解决方案:
public async Task SendAsync(IdentityMessage message)
{
...
using (var smtpClient = new SmtpClient())
{
smtpClient.EnableSsl = true;
smtpClient.SendCompleted += (s, e) => { smtpClient.Dispose(); };
await smtpClient.SendMailAsync(msg);
}
}
所以,让我们解决原来的问题:
如果我使用等待应用程序只是永远运行:没有错误消息。
我感觉您的代码在调用Task.Wait
后可能正在调用Task<T>.Result
或SendAsync
。这是真正的问题:调用代码应该更改为使用await
(这使得调用方法async
并返回Task
/ Task<T>
,这使得 调用代码必须使用await
等)。允许async
通过代码库自然地“增长”。这就是为什么我MSDN article on the subject中的async
最佳做法之一是“一直异步”。
导致死锁是因为当您await
任务时,await
默认会捕获“当前上下文”(在本例中为ASP.NET SynchronizationContext
),以及使用该上下文来恢复async
方法。但是,ASP.NET SynchronizationContext
一次只允许每个请求一个线程,因此当调用代码阻止时(通过调用Wait
或Result
) ,它在该上下文中阻塞了一个线程,async
方法无法恢复。我更详细地描述了这种死锁情况on my blog。
最佳解决方案是使用await
,并一直使用async
。
答案 1 :(得分:1)
可能问题是,当执行SendMailAsync时,您的smpt客户端已经被释放。这就是抛出异常的原因,但是因为此操作在单独的线程(异步)中运行,所以会得到TaskCanceledException。
您可以尝试删除使用声明。
public Task SendAsync(IdentityMessage message)
{
// Plug in your email service here to send an email.
try
{
#region formatter
string text = string.Format("Please click on this link to {0}: {1}", message.Subject, message.Body);
string html = "Please confirm your account by clicking this link: <a href=\"" + message.Body + "\">link</a><br/>";
html += HttpUtility.HtmlEncode(@"Or click on the copy the following link on the browser:" + message.Body);
#endregion
MailMessage msg = new MailMessage();
msg.From = new MailAddress("info@emailadress.com");
msg.To.Add(new MailAddress(message.Destination));
msg.Subject = message.Subject;
msg.AlternateViews.Add(AlternateView.CreateAlternateViewFromString(text, null, MediaTypeNames.Text.Plain));
msg.AlternateViews.Add(AlternateView.CreateAlternateViewFromString(html, null, MediaTypeNames.Text.Html));
var smtpClient = new SmtpClient();
smtpClient.EnableSsl = true;
smtpClient.SendCompleted += (s, e) => { smtpClient.Dispose(); };
return smtpClient.SendMailAsync(msg);
}
catch (Exception)
{
return Task.FromResult(0);
}
}
Smtp客户端将在SendCompleted处理程序的帮助下处理。