Ich versuche, E-Mail-Bestätigung für eine ASP.NET MVC5-Website, basierend auf dem Beispiel AccountController von der VS2013-Projektvorlage einrichten. Ich habe die IIdentityMessageService
SmtpClient
mit implementiert, versucht, es zu halten so einfach wie möglich:SmtpClient.SendMailAsync verursacht Deadlock beim Auslösen einer bestimmten Ausnahme
public class EmailService : IIdentityMessageService
{
public async Task SendAsync(IdentityMessage message)
{
using(var client = new SmtpClient())
{
var mailMessage = new MailMessage("[email protected]", message.Destination, message.Subject, message.Body);
await client.SendMailAsync(mailMessage);
}
}
}
Der Controller-Code, der es anruft, ist gerade aus der Vorlage (in einer separaten Aktion extrahiert, da wollte ich andere ausschließen möglich) verursacht:
public async Task<ActionResult> TestAsyncEmail()
{
Guid userId = User.Identity.GetUserId();
string code = await UserManager.GenerateEmailConfirmationTokenAsync(userId);
var callbackUrl = Url.Action("ConfirmEmail", "Account", new { userId = userId, code = code }, protocol: Request.Url.Scheme);
await UserManager.SendEmailAsync(userId, "Confirm your account", "Please confirm your account by clicking <a href=\"" + callbackUrl + "\">here</a>");
return View();
}
aber ich merkwürdiges Verhalten bin immer, wenn die E-Mail zu senden, nicht aber nur in einem speziellen Fall, wenn der Host irgendwie nicht erreichbar ist. Beispiel config:
In diesem Fall scheint die Anforderung Deadlock, nie etwas an den Client zurückgeben. Wenn die E-Mail aus einem anderen Grund nicht gesendet wird (z. B. wenn der Host die Verbindung aktiv verweigert), wird die Ausnahme normal verarbeitet und ich erhalte eine YSOD.
Mit Blick auf die Windows-Ereignisprotokolle scheint es, dass ein InvalidOperationException
um denselben Zeitrahmen geworfen wird, mit der Nachricht "Ein asynchrones Modul oder Handler wurde abgeschlossen, während ein asynchroner Vorgang noch ausstehend war."; Ich bekomme die gleiche Nachricht in einem YSOD, wenn ich versuche, die SmtpException
in der Steuerung zu fangen und eine im catch-Block zurückgeben. Also ich denke, die await
-ed Operation kann in beiden Fällen nicht abgeschlossen werden.
Soweit ich das beurteilen kann, befolge ich alle async/erwarten Best Practices wie in anderen Posts auf SO beschrieben (z. B. HttpClient.GetAsync(...) never returns when using await/async), hauptsächlich "mit async/warten den ganzen Weg". Ich habe auch versucht, ConfigureAwait(false)
, ohne Änderung zu verwenden. Da der Code nur dann Deadlocks auslöst, wenn eine bestimmte Ausnahme ausgelöst wird, stelle ich fest, dass das allgemeine Muster in den meisten Fällen korrekt ist, aber intern etwas passiert, was es in diesem Fall falsch macht; aber da ich ziemlich neu in der Programmierung bin, habe ich das Gefühl, dass ich falsch liegen könnte.
Gibt es etwas, was ich falsch mache? Ich kann immer einen synchronen Anruf (dh. SmtpClient.Send()
) in der SendAsync-Methode verwenden, aber es fühlt sich so an, als ob das so funktionieren sollte.
einen Blick auf Nehmen Sie [Stephen Cleary Antwort auf den Fang eine Ausnahme auf einem void-Methode ('SendMailAsync')] (http://stackoverflow.com/a/7350534/ 209259). Async Void Methods sind manchmal problematische Kinder. –
@ErikPhilips - Ich sehe keine 'async void' Methoden in der Probe (entweder implementiert oder aufgerufen) - meinst du eine bestimmte Zeile? –
Als Workaround können Sie versuchen, den Host manuell aufzulösen und früher zu scheitern ... Schauen Sie auch [die Quelle] (http://referencesource.microsoft.com/#System/net/System/Net/mail/SmtpClient.cs, b9a40a3be18a4d58) für Einblicke - hoffentlich wäre es hilfreich ... –