ASP.NET Identity发送电子邮件:TaskCanceledException

时间:2014-08-14 11:42:12

标签: c# asp.net .net asp.net-identity smtpclient

我尝试在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();
        }
    }

2 个答案:

答案 0 :(得分:3)

@ntl正确识别了问题:SmtpClient实例在请求完成之前正在处理。不过,我更喜欢使用asyncawait作为解决方案:

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>.ResultSendAsync。这是真正的问题:调用代码应该更改为使用await(这使得调用方法async并返回Task / Task<T>,这使得 调用代码必须使用await等)。允许async通过代码库自然地“增长”。这就是为什么我MSDN article on the subject中的async最佳做法之一是“一直异步”。

导致死锁是因为当您await任务时,await默认会捕获“当前上下文”(在本例中为ASP.NET SynchronizationContext),以及使用该上下文来恢复async方法。但是,ASP.NET SynchronizationContext一次只允许每个请求一个线程,因此当调用代码阻止时(通过调用WaitResult) ,它在该上下文中阻塞了一个线程,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处理程序的帮助下处理。