.NET 4.0 altında SmtpClient, SendAsync ve Dispose kullanmak için en iyi yöntemler nelerdir?


116

SmtpClient'i artık tek kullanımlık olduğu için, özellikle de SendAsync kullanarak arama yaparsam nasıl yöneteceğim konusunda biraz kafam karıştı. Muhtemelen SendAsync tamamlanana kadar Dispose çağırmamalıyım. Ama onu hiç çağırmalı mıyım (örneğin, "kullanma" kullanarak). Senaryo, çağrılar yapıldığında periyodik olarak e-posta gönderen bir WCF hizmetidir. Hesaplamanın çoğu hızlıdır, ancak e-postanın gönderilmesi bir saniye kadar sürebilir, bu nedenle Async tercih edilir.

Her posta gönderdiğimde yeni bir SmtpClient oluşturmalı mıyım? Tüm WCF için bir tane oluşturmalı mıyım? Yardım!

Güncelleme Bir fark yaratması durumunda, her e-posta her zaman kullanıcıya göre özelleştirilir. WCF, Azure'da barındırılır ve posta gönderisi olarak Gmail kullanılır.


1
IDisposable ve async ile nasıl başa çıkılacağına dair daha büyük resim hakkındaki bu gönderiye bakın: stackoverflow.com/questions/974945/…
Chris Haas

Yanıtlar:


139

Not: .NET 4.5 SmtpClient async awaitableyöntemi uygular SendMailAsync. Daha düşük sürümler için SendAsyncaşağıda açıklandığı gibi kullanın .


IDisposableÖrnekleri her zaman mümkün olan en erken zamanda atmalısınız . Eşzamansız aramalarda bu, mesaj gönderildikten sonra geri aramadadır.

var message = new MailMessage("from", "to", "subject", "body"))
var client = new SmtpClient("host");
client.SendCompleted += (s, e) => {
                           client.Dispose();
                           message.Dispose();
                        };
client.SendAsync(message, null);

SendAsyncGeri aramayı kabul etmemesi biraz can sıkıcı .


son satırın 'beklemesi' gerekmez mi?
niico

20
Bu kod daha önce awaitmevcut değildi. Bu, olay işleyicilerini kullanan geleneksel bir geri aramadır. awaityenisi kullanılıyorsa kullanılmalıdır SendMailAsync.
TheCodeKing

3
SmtpException: Posta gönderme hatası .--> System.InvalidOperationException: Şu anda zaman uyumsuz bir işlem başlatılamaz. Eşzamansız işlemler yalnızca eşzamansız bir işleyici veya modül içinde veya Sayfa yaşam döngüsündeki belirli olaylar sırasında başlatılabilir. Bir Sayfa yürütülürken bu istisna meydana gelirse, Sayfanın <% @ Page Async = "true"%> olarak işaretlendiğinden emin olun. Bu istisna, genellikle ASP.NET istek işleme içinde desteklenmeyen "zaman uyumsuz geçersiz" yöntemini çağırma girişimini de gösterebilir. Bunun yerine, zaman uyumsuz yöntem bir Görev döndürmeli ve arayan bunu beklemelidir.
Mrchief

1
nullİkinci parametre olarak belirtmek güvenli SendAsync(...)midir?
jocull

167

Orijinal soru .NET 4 için sorulmuştu, ancak eğer .NET 4.5'ten itibaren yardımcı olursa SmtpClient zaman uyumsuz beklenebilir yöntemi uygular SendMailAsync.

Sonuç olarak, e-postayı eşzamansız olarak göndermek aşağıdaki gibidir:

public async Task SendEmail(string toEmailAddress, string emailSubject, string emailMessage)
{
    using (var message = new MailMessage())
    {
        message.To.Add(toEmailAddress);

        message.Subject = emailSubject;
        message.Body = emailMessage;

        using (var smtpClient = new SmtpClient())
        {
            await smtpClient.SendMailAsync(message);
        }
    }
}

SendAsync yöntemini kullanmaktan kaçınmak daha iyidir.


Bundan kaçınmak neden daha iyidir? Bunun gereksinimlere bağlı olduğunu düşünüyorum.
Jowen

14
SendMailAsync () yine de SendAsync () yöntemi etrafında bir sarmalayıcıdır. async / await çok daha temiz ve zariftir. Tam olarak aynı gereksinimleri karşılayacaktır.
Boris Lipschitz

2
@RodHartzell'i her zaman kullanabilirsiniz .ContinueWith ()
Boris Lipschitz

2
Kullanmak veya atmak daha mı iyi? Yoksa pratik bir fark yok mu? SendMailAsync çalıştırılmadan önce smtpClient'in elden çıkarılması son 'kullanma' bloğunda mümkün değil mi?
niico

6
MailMessageAyrıca Atılmalıdır.
TheCodeKing

16

Genel olarak, ID atılabilir nesneler mümkün olan en kısa sürede atılmalıdır; IDisposable'ın bir nesneye uygulanması, söz konusu sınıfın deterministik olarak serbest bırakılması gereken pahalı kaynakları barındırdığı gerçeğini iletmeyi amaçlamaktadır. Bununla birlikte, bu kaynakları oluşturmak pahalıysa ve bu nesnelerin çoğunu oluşturmanız gerekiyorsa, bir örneği bellekte tutmak ve yeniden kullanmak daha iyi (performans açısından) daha iyi olabilir. Bunun herhangi bir fark yaratıp yaratmadığını bilmenin tek bir yolu var: profilinin profilini çıkarın!

Re: atma ve Async: usingAçıkçası kullanamazsınız . Bunun yerine, genellikle nesneyi SendCompleted olayına atarsınız:

var smtpClient = new SmtpClient();
smtpClient.SendCompleted += (s, e) => smtpClient.Dispose();
smtpClient.SendAsync(...);

6

Tamam, eski soru biliyorum. Ama buna benzer bir şey uygulamaya ihtiyacım olduğunda kendimle karşılaştım. Sadece biraz kod paylaşmak istedim.

Eşzamansız olarak birkaç posta göndermek için birkaç SmtpClients üzerinde yineliyorum. Çözümüm TheCodeKing'e benzer, ancak bunun yerine geri arama nesnesini atıyorum. Ayrıca MailMessage'ı SendCompleted olayında almak için userToken olarak geçiriyorum, böylece dispose'ı da arayabilirim. Bunun gibi:

foreach (Customer customer in Customers)
{
    SmtpClient smtpClient = new SmtpClient(); //SmtpClient configuration out of this scope
    MailMessage message = new MailMessage(); //MailMessage configuration out of this scope

    smtpClient.SendCompleted += (s, e) =>
    {
        SmtpClient callbackClient = s as SmtpClient;
        MailMessage callbackMailMessage = e.UserState as MailMessage;
        callbackClient.Dispose();
        callbackMailMessage.Dispose();
    };

    smtpClient.SendAsync(message, message);
}

2
Gönderilecek her e-posta için yeni bir SmtpClient oluşturmak en iyi yöntem mi?
Martín Coll

1
Evet, istemciyi geri
aramada bıraktığınız

1
Teşekkürler! ve sadece kısa bir açıklama uğruna: www.codefrenzy.net/2012/01/30/how-asynchronous-is-smtpclient-sendasync
Martín Coll

1
Bu, smtpclient.sendAsync işlevi ve ilgili atma işlemi için stackoverflow'da bulduğum en basit ve doğru yanıtlardan biridir . Eşzamansız bir toplu posta gönderme kitaplığı yazdım. Her birkaç dakikada bir 50+ mesaj gönderdiğim için dispose yöntemini uygulamak benim için çok önemli bir adımdı. Bu kod tam olarak bunu başarmama yardımcı oldu. Çoklu iş parçacığı ortamları sırasında bu kodda bazı hatalar bulursam cevap vereceğim.
vibs2006

1
Exchange sunucusunu yapılandırma yeteneğiniz yoksa (kullanıyorsanız) bir döngüde 100'den fazla e-posta gönderirken bunun iyi bir yaklaşım olmadığını söyleyebilirim. Sunucu gibi istisna atabilir 4.3.2 The maximum number of concurrent connections has exceeded a limit, closing trasmission channel. Bunun yerine yalnızca bir örneğini kullanmayı deneyinSmtpClient
ibubi

6

SmtpClient'i atmanın neden özellikle önemli olduğunu aşağıdaki yorumla görebilirsiniz:

public class SmtpClient : IDisposable
   // Summary:
    //     Sends a QUIT message to the SMTP server, gracefully ends the TCP connection,
    //     and releases all resources used by the current instance of the System.Net.Mail.SmtpClient
    //     class.
    public void Dispose();

İstemciyi elden çıkarmadan Gmail kullanarak birden çok posta gönderme senaryomda, şunları elde ederdim:

Mesaj: Hizmet mevcut değil, aktarım kanalı kapatılıyor. Sunucu yanıtı şöyleydi: 4.7.0 Geçici Sistem Sorunu. Daha sonra tekrar deneyin (WS). oo3sm17830090pdb.64 - gsmtp


1
Şimdiye kadar SMTP İstemcileri gönderirken istisnanızı burada paylaştığınız için teşekkür ederiz. Kendi SMTP Sunucumu kullanmama rağmen, her zaman iyi bir programlama uygulaması düşünülmelidir. Sizin hatanıza bakarak şimdi uyarılar aldım ve kodumu platform güvenilirliğini sağlamak için elden çıkarma işlevlerini içerecek şekilde düzelteceğim.
vibs2006
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.