Было много полезных советов, за которые я проголосовал здесь ... например, обязательно не забывайте использовать IDisposable (я совершенно не знал). Я также понял, как важно вручную отлавливать ошибки, когда в другом потоке нет контекста - я работал над теорией, согласно которой я должен просто позволить ELMAH обрабатывать все. Кроме того, дальнейшие исследования позволили мне понять, что я забыл использовать IDisposable и в почтовом сообщении.
В ответ Ричарду, хотя я вижу, что многопоточное решение может работать (как предложено в моем первом примере), пока я ловлю ошибки ... есть еще кое-что пугающее в том факте, что IIS полностью взрывается, если это ошибка не обнаружена Это говорит мне о том, что ASP.NET/IIS никогда не предназначался для вас, чтобы сделать это ... именно поэтому я склоняюсь к тому, чтобы продолжать использовать .BeginInvoke / делегаты, так как это не портит IIS, когда что-то идет не так и, кажется, быть более популярным в ASP.NET.
Отвечая на вопрос ASawyer, я был совершенно удивлен, что в SMTP-клиент встроен .SendAsync. Я какое-то время играл с этим решением, но, похоже, оно мне не подходит. Хотя я могу пропустить клиент кода, который выполняет SendAsync, страница все еще «ждет», пока не завершится событие SendCompleted. Моя цель состояла в том, чтобы пользователь и страница двигались вперед, пока электронное письмо отправляется в фоновом режиме. У меня есть ощущение, что я все еще могу что-то делать не так ... поэтому, если кто-то приходит по этому поводу, он может попробовать это сам.
Вот мое полное решение для 100% асинхронной отправки писем в дополнение к журналу ошибок ELMAH.MVC. Я решил пойти с расширенной версией примера 2:
public void SendThat(MailMessage message)
{
AsyncMethodCaller caller = new AsyncMethodCaller(SendMailInSeperateThread);
AsyncCallback callbackHandler = new AsyncCallback(AsyncCallback);
caller.BeginInvoke(message, callbackHandler, null);
}
private delegate void AsyncMethodCaller(MailMessage message);
private void SendMailInSeperateThread(MailMessage message)
{
try
{
SmtpClient client = new SmtpClient();
client.Timeout = 20000; // 20 second timeout... why more?
client.Send(message);
client.Dispose();
message.Dispose();
// If you have a flag checking to see if an email was sent, set it here
// Pass more parameters in the delegate if you need to...
}
catch (Exception e)
{
// This is very necessary to catch errors since we are in
// a different context & thread
Elmah.ErrorLog.GetDefault(null).Log(new Error(e));
}
}
private void AsyncCallback(IAsyncResult ar)
{
try
{
AsyncResult result = (AsyncResult)ar;
AsyncMethodCaller caller = (AsyncMethodCaller)result.AsyncDelegate;
caller.EndInvoke(ar);
}
catch (Exception e)
{
Elmah.ErrorLog.GetDefault(null).Log(new Error(e));
Elmah.ErrorLog.GetDefault(null).Log(new Error(new Exception("Emailer - This hacky asynccallback thing is puking, serves you right.")));
}
}