2017-07-13 6 views
0

背景SmtpClient SendAsyncエラー

私はメールのRabbitMQのキューを監視小さなコンソールアプリケーションを書かれています。電子メールがキューにプッシュされると、私のアプリケーションはその電子メールを受け取って処理し、送信します。

コードの下

それは実際に電子メールを送信するもので、私の電子メールサービスのためのコードです。

public class MailService : IMailService 
{ 
    private readonly SmtpClient _smtpClient = new SmtpClient(); 

    public void SendEmail(string toEmail, string subject, string body, bool isHtml) 
    { 
     var emailMessage = BuildEmailMessage(toEmail.Trim(), subject.Trim(), body, isHtml); 
     _smtpClient.SendAsync(emailMessage, null); 
    } 

    #region Helpers 
    private static MailMessage BuildEmailMessage(string toEmail, string subject, string body, bool isHtml) 
    { 
     const string fromEmailAddress = "[email protected]"; 
     var emailMessage = new MailMessage(fromEmailAddress, toEmail, subject, body) { IsBodyHtml = isHtml }; 
     return emailMessage; 
    } 
    #endregion 
} 

PROBLEM

は私がRabbitMQのキューに2通のメールを持っていた、と私は私のコンソールアプリケーションの消費者を解雇する場合、それは(私はその第一の電子メールを受信した最初のメールを送信した後、次の例外がスローされました私の受信箱)。

非同期呼び出しはすでに進行中です。このメソッドを呼び出すには、完了またはキャンセルする必要があります。

私はいくつかの掘り出し物を見、this threadがこれを得た理由を説明しました。

SendAsyncを呼び出した後、あなたが送信またはSendAsync使用して、別の電子メールメッセージを送信しようとする前に完了するために、電子メールの送信を待機しなければならない:どうやらSendAsync()を使用しているため移動するための方法ではありません。

だから、これについてお勧めの方法は何ですか?私はメールごとにSmtpClientクラスの新しいインスタンスを作成できますが、それは本当に良いアイデアですか?

+0

なぜ最初に 'SendAsync'を使用していますか?ちょうど 'Send'を使用してください。本当に非同期が必要な場合は、コードをリファクタリングして 'await SendMailAsync'を呼び出す必要があります。つまり、呼び出しスタックのすべての部分を非同期にする必要があります。 – mason

+0

@masonあなたは私のコードをそのまま残し、単に '.Send(message)'を使うか、 'using()'ステートメントでそれを囲むべきですか? – Ciwan

+0

'SmtpClient'をフィールドとして保持するのではなく、usingステートメントで囲む方がよいでしょう。しかし、古いAsyncパターンに基づく 'SendAsync'の代わりに' .Send(message) 'を使用してください。 – mason

答えて

1

電子メールを同期して送信するには、古い非同期構文SendAsyncを使用する必要はありません。また、SmtpClientが、一度に1つのスレッドからのみヒットするようにするには、usingステートメントでラップしてください。パフォーマンスには多少のペナルティがありますが、大量のメールを送信しない限り、気づかないでしょう。そして、あなたが1トンを送っているなら、MailService.SendEmailメソッドをオーバーロードしてIEnumerable<EmailModel>を受け入れ、それらを一度に1つのSmtpClientを使って送ります。

public void SendEmail(string toEmail, string subject, string body, bool isHtml) 
{ 
    var emailMessage = BuildEmailMessage(toEmail.Trim(), subject.Trim(), body, isHtml); 

    using(var client = new SmtpClient()) 
    { 
     _smtpClient.Send(emailMessage); 
    }  
} 

//this would be the right way to do async 
public async Task SendEmailAsync(string toEmail, string subject, string body, bool isHtml) 
{ 
    var emailMessage = BuildEmailMessage(toEmail.Trim(), subject.Trim(), body, isHtml); 

    using(var client = new SmtpClient()) 
    { 
     _smtpClient.SendMailAsync(emailMessage); 
    }  
} 


//this would be the right way to do multiple emails 
//you'd need to create an EmailModel class to contain all the details for each email (similar to MailMessage, but it would prevent your own code from taking a dependency on System.Net.Mail 
public void SendEmail(IEnumerable<EmailModel> emailModels) 
{ 

    var mailMessages = emailModels.Select(em => ConvertEmailModelToMailMessage(em)); 

    using(var client = new SmtpClient()) 
    { 
     foreach(var mailMessage in mailMessages) 
     { 
      //you may want some error handling on the below line depending on whether you want all emails to attempt to send even if one encounters an error 
      _smtpClient.Send(mailMessage); 
     } 
    }  
} 

private MailMessage ConvertEmailModelToMailMessage(EmailModel emailModel) 
{ 
    //do conversion here 
} 
関連する問題