Sto tentando di impostare la conferma dell'e-mail per un sito Web ASP.NET MVC5, in base all'esempio AccountController del modello di progetto VS2013. Ho implementato il IIdentityMessageService
utilizzando SmtpClient
, cercando di tenerlo il più semplice possibile:SmtpClient.SendMailAsync causa deadlock quando si genera un'eccezione specifica
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);
}
}
}
Il codice di controllo che chiama esso è direttamente dal modello (estratto in un'azione separata dato che volevo escludere altre possibili provoca):
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();
}
Tuttavia mi sto comportamento strano quando la posta non riesce a inviare, ma solo in un caso specifico, quando l'host è in qualche modo irraggiungibile. Esempio di configurazione:
<system.net>
<mailSettings>
<smtp deliveryMethod="Network">
<network host="unreachablehost" defaultCredentials="true" port="25" />
</smtp>
</mailSettings>
</system.net>
In tal caso, la richiesta sembra bloccata, non restituire mai nulla al client. Se la posta non riesce a inviare per qualsiasi altra ragione (ad esempio l'host rifiuta attivamente la connessione), l'eccezione viene gestita normalmente e io ottengo un YSOD.
Guardando i registri degli eventi di Windows, sembra che uno InvalidOperationException
venga lanciato intorno allo stesso periodo di tempo, con il messaggio "Un modulo o un handler asincrono completato mentre un'operazione asincrona era ancora in sospeso."; Ottengo lo stesso messaggio in un YSOD se provo a catturare lo SmtpException
nel controller e restituisco un ViewResult
nel blocco catch. Quindi immagino che l'operazione await
-ed non sia completata in entrambi i casi.
Per quanto posso dire, sto seguendo tutte le best practice asincrone/attese come delineato in altri post su SO (ad esempio HttpClient.GetAsync(...) never returns when using await/async), principalmente "usando async/attendo fino in fondo". Ho anche provato a utilizzare ConfigureAwait(false)
, senza modifiche. Dato che il codice si blocca solo se viene generata un'eccezione specifica, immagino che lo schema generale sia corretto per la maggior parte dei casi, ma qualcosa sta accadendo internamente che lo rende scorretto in quel caso; ma dato che sono abbastanza nuovo alla programmazione simultanea, ho la sensazione che potrei sbagliarmi.
C'è qualcosa che sto facendo male? Posso sempre usare una chiamata sincrona (ovvero SmtpClient.Send()
) nel metodo SendAsync, ma sembra che questo dovrebbe funzionare così com'è.
Date un'occhiata a [risposta di Stephen Cleary sulla cattura un'eccezione su un metodo void ('SendMailAsync')] (http://stackoverflow.com/a/7350534/ 209.259). I metodi Void asincroni sono a volte bambini problematici. –
@ErikPhilips - Non vedo alcun metodo 'async void' nell'esempio (implementato o chiamato) - intendevi qualche riga particolare? –
Come soluzione temporanea, puoi provare a risolvere manualmente l'host e fallire prima ... Guarda anche [l'origine] (http://referencesource.microsoft.com/#System/net/System/Net/mail/SmtpClient.cs, b9a40a3be18a4d58) per approfondimenti - si spera che sarebbe utile ... –