2012-01-07 5 views
8

Prosta koncepcja tutaj. Dotyczy to witryny budowanej przy użyciu MVC 3 i Entity Framework 4. Po zarejestrowaniu użytkownika w serwisie, na jego adres e-mail wysyłana jest wiadomość e-mail. Po raz pierwszy zaimplementowałem to za pomocą SmtpClient.Send() i działało dobrze. Potem wpadłem na pomysł, aby spróbować asynchronicznie wysłać wiadomość e-mail. Mam problemy z dwoma podejściami asynchronicznymi, których próbowałem.Dwa sposoby wysyłania wiadomości e-mail przez SmtpClient asynchronicznie, różne wyniki

Pierwsza implementacja (z tego postu bez odpowiedzi: https://stackoverflow.com/questions/7558582/how-to-dispose-using-smtpclient-send-and-asynccallback):

public bool Emailer(){ 
    . 
    . 
    . 
    using (var smtpClient = new SmtpClient()) 
    { 
     smtpClient.EnableSsl = true; 
     smtpClient.Host = "smtp.gmail.com"; 
     smtpClient.Port = 587; 
     smtpClient.UseDefaultCredentials = false; 
     smtpClient.Credentials = new NetworkCredential("[email protected]", "mypassword"); 

     var sd = new SendEmailDelegate(smtpClient.Send); 
     var cb = new AsyncCallback(SendEmailResponse); 
     sd.BeginInvoke(message, cb, sd); 

     return true; 
    } 
} 

private delegate void SendEmailDelegate(System.Net.Mail.MailMessage m); 

private static void SendEmailResponse(IAsyncResult ar) 
{ 
    try 
    { 
     SendEmailDelegate sd = (SendEmailDelegate)(ar.AsyncState); 
     sd.EndInvoke(ar); // "cannot access a disposed object" errors here 
    } 
    catch (Exception e) 
    { 
     _logger.WarnException("Error on EndInvoke.", e); 
    } 
} 

To działało przez połowę czasu. W drugiej połowie dostanę błąd "Nie można uzyskać dostępu do obiektu" w CallBack.

Następny realizacja (od członka z solidnej reputacji: What are best practices for using SmtpClient, SendAsync and Dispose under .NET 4.0):

var smtpClient = new SmtpClient(); 
smtpClient.EnableSsl = true; 
smtpClient.Host = "smtp.gmail.com"; 
smtpClient.Port = 587; 
smtpClient.UseDefaultCredentials = false; 
smtpClient.Credentials = new NetworkCredential("[email protected]", "mypassword"); 

smtpClient.SendCompleted += (s, e) => 
    { 
     smtpClient.Dispose(); 
     message.Dispose(); 
    }; 
smtpClient.SendAsync(message, null); 

Dzięki tej implementacji ja nie otrzymuję żadnych błędów, ale jest zauważalnie dłużej opóźnienie (~ 5 sekund) w trybie debugowania podczas smtpclient. Wykonywana jest funkcja SendAsync(), co pozwala mi sądzić, że nie jest ona wysyłana asynchronicznie.

Pytania:

1), co jest złego w pierwszej metodzie, która jest przyczyną błędów „zbyte obiekt”?

2) czy druga implementacja ma problem powodujący, że wiadomość e-mail nie jest wysyłana asynchronicznie? Czy opóźnienie 5 sekund jest bez znaczenia?

Może być również ważne, aby pamiętać, że chociaż witryna nie musi obsługiwać wysyłania dużej liczby wiadomości e-mail (tylko rejestracja użytkownika, opcjonalne biuletyny itp.), Przewidujemy dużą liczbę użytkowników w przyszłości, stąd moja decyzja o asynchronicznym wysyłaniu e-maili.

Dzięki.

Odpowiedz

7

Twoja metoda pięści nie zadziała poprawnie z powodu bloku USING. Po zakończeniu używania bloku, obiekt SmtpClient zostanie usunięty. Więc nie możesz uzyskać do niego dostęp w swojej obsłudze zdarzeń.

+0

Dzięki za odpowiedź. Teraz widzę, że obiekt jest usuwany, zanim przewodnik ma szansę wystrzelić. Rozumiem również, że oba SmtpClient muszą zostać usunięte, a EndInvoke musi zostać wywołany. Czy osiąga to podejście nr 2? –

5

porad: zastosowanie 1-dont "za pomocą bloku" dla MailMessage Objet, to zbyć swój przedmiot przed mail wysłany
2-wyrzucać przedmioty MailMessage na SmtpClient.SendCompleted imprezy:

smtpClient.SendCompleted += (s, e) => 
    { 
     message.Dispose(); 
    }; 

3-set SendCompletedEventHandler dla obiektu smtpclient

smtpClient.SendCompleted += new SendCompletedEventHandler(SendCompletedCallback); 

4-dodatkowy kod:

private static void SendCompletedCallback(object sender, AsyncCompletedEventArgs e) 
    { 
     // Get the unique identifier for this asynchronous operation. 
     String token = (string)e.UserState; 

     if (e.Cancelled) 
     { 
      //write your code here 
     } 
     if (e.Error != null) 
     { 
      //write your code here 
     } 
     else //mail sent 
     { 
      //write your code here 
     } 

     mailSent = true; 
    } 
1

SmtpClient.SendAsync to preferowana metoda asynchronicznego wysyłania wiadomości e-mail, ponieważ korzysta ona z metod specjalnie zaprojektowanych do tego celu. Jest również łatwiejsze do wdrożenia i udowodniono, że działa tysiące razy.

Twoje 5-sekundowe opóźnienie jest dziwne i sugeruje, że istnieje problem wymagający adresowania. Pierwszy fragment kodu właśnie rozwiązuje problem, ale go nie eliminuje.

SmtpClient.SendAsync rzeczywiście tylko wysłać asynchronicznie jeśli metoda dostawy jest nieSpecifiedPickupDirectory lub PickupDirectoryFromIis. W takich przypadkach zapisze plik wiadomości w folderze pobierania przed powrotem. Sprawdź sekcję <smtp> pliku konfiguracyjnego. Zgaduję, że używasz jednej z tych metod, a problem dotyczy folderu pobierania. Usuń stare pliki, które możesz tam mieć i sprawdź, czy problem nie dotyczy Twojego oprogramowania antywirusowego, które najprawdopodobniej przeszukuje każdy nowy plik pod kątem wirusów. Sprawdź, czy są ustawione atrybuty szyfrowania lub kompresji. Może być też czymś innym. Najlepszym sposobem sprawdzenia, czy źródłem problemów jest folder, jest ręczne skopiowanie do niego pliku wiadomości e-mail.