web-dev-qa-db-ja.com

.NET 4.0でSmtpClient、SendAsync、およびDisposeを使用するためのベストプラクティスは何ですか

特にSendAsyncを使用して呼び出しを行う場合、SmtpClientを使い捨てできるようになったため、SmtpClientの管理方法について少し困惑しています。おそらく、SendAsyncが完了するまでDisposeを呼び出さないでください。しかし、これを呼び出す必要があります(たとえば、「使用」を使用)。このシナリオは、呼び出しが行われたときに定期的に電子メールを送信するWCFサービスです。ほとんどの計算は高速ですが、電子メールの送信には数秒かかる場合があるため、非同期が望ましいでしょう。

メールを送信するたびに新しいSmtpClientを作成する必要がありますか? WCF全体に1つ作成する必要がありますか?助けて!

更新違いが生じる場合、各メールは常にユーザーに合わせてカスタマイズされます。 WCFはAzureでホストされ、Gmailはメーラーとして使用されます。

108
tofutim

注:.NET 4.5 SmtpClientはasync awaitableメソッドSendMailAsyncを実装しています。下位バージョンの場合は、以下で説明するようにSendAsyncを使用します。


IDisposableインスタンスは、できるだけ早く廃棄する必要があります。非同期呼び出しの場合、これはメッセージが送信された後のコールバックにあります。

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);

SendAsyncがコールバックを受け入れないのは少し面倒です。

127
TheCodeKing

元の質問は.NET 4に対して尋ねられましたが、それが.NET 4.5の時点で役立つ場合、SmtpClientは非同期待機可能メソッド SendMailAsync を実装します。

その結果、非同期で電子メールを送信することは次のとおりです。

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メソッドの使用は避けたほうがよいでしょう。

159
Boris Lipschitz

一般に、IDisposableオブジェクトはできるだけ早く破棄する必要があります。オブジェクトにIDisposableを実装することは、問題のクラスが決定的に解放されるべき高価なリソースを保持しているという事実を伝えることを目的としています。ただし、これらのリソースの作成に費用がかかり、これらのオブジェクトを大量に作成する必要がある場合は、1つのインスタンスをメモリに保持して再利用する方が(パフォーマンス面で)優れている場合があります。それが違いを生むかどうかを知る唯一の方法があります。それをプロファイリングしてください!

Re:破棄と非同期:usingは明らかに使用できません。代わりに、通常、SendCompletedイベントでオブジェクトを破棄します。

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

SmtpClientを破棄することが特に重要である理由は、次のコメントで確認できます。

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();

クライアントを破棄せずにGmailを使用して複数のメールを送信する私のシナリオでは、次のものを入手していました。

メッセージ:サービスを利用できません。伝送チャネルを閉じています。サーバーの応答:4.7.0一時的なシステムの問題。後でもう一度試してください(WS)。 oo3sm17830090pdb.64-gsmtp

6

わかった、古い質問。しかし、似たようなものを実装する必要があるときに、私は自分でこれを見つけました。コードを共有したかっただけです。

複数のメールを非同期に送信するために、複数のSmtpClientを繰り返し処理しています。私のソリューションはTheCodeKingに似ていますが、代わりにコールバックオブジェクトを破棄しています。また、MailMessageをuserTokenとして渡して、SendCompletedイベントで取得し、disposeを呼び出すこともできます。このような:

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);
}
6
jmelhus