.NET 4.0에서 SmtpClient, SendAsync 및 Dispose를 사용하는 모범 사례는 무엇입니까?


116

특히 SendAsync를 사용하여 호출하는 경우 일회용이므로 SmtpClient를 관리하는 방법에 대해 약간 당황합니다. 아마도 SendAsync가 완료 될 때까지 Dispose를 호출해서는 안됩니다. 그러나 나는 그것을 호출해야한다 (예 : "사용"사용). 시나리오는 호출이 이루어질 때 주기적으로 이메일을 발송하는 WCF 서비스입니다. 대부분의 계산은 빠르지 만 이메일 전송에는 1 초 정도 걸릴 수 있으므로 Async가 더 좋습니다.

메일을 보낼 때마다 새 SmtpClient를 만들어야합니까? 전체 WCF에 대해 하나를 만들어야합니까? 도움!

업데이트 차이가있을 경우 각 이메일은 항상 사용자에게 맞춤 설정됩니다. WCF는 Azure에서 호스팅되고 Gmail은 메일러로 사용됩니다.


1
IDisposable 및 비동기를 처리하는 방법에 대한 더 큰 그림에 대한이 게시물을 참조하십시오. stackoverflow.com/questions/974945/…
Chris Haas

답변:


139

참고 : .NET 4.5 SmtpClient는 async awaitablemethod 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콜백을 수락하지 않는 것이 약간 짜증납니다 .


마지막 줄에 'await'가 있어야하지 않습니까?
niico

20
이 코드는 이전 await에 작성되지 않았습니다 . 이것은 이벤트 핸들러를 사용하는 전통적인 콜백입니다. await최신 SendMailAsync.
TheCodeKing

3
SmtpException : 메일 전송 실패 .--> System.InvalidOperationException : 현재 비동기 작업을 시작할 수 없습니다. 비동기 작업은 비동기 처리기 또는 모듈 내에서 또는 페이지 수명주기의 특정 이벤트 동안에 만 시작할 수 있습니다. 페이지를 실행하는 동안이 예외가 발생한 경우 페이지가 <% @ Page Async = "true"%>로 표시되어 있는지 확인하십시오. 이 예외는 일반적으로 ASP.NET 요청 처리 내에서 지원되지 않는 "async void"메서드를 호출하려는 시도를 나타낼 수도 있습니다. 대신 비동기 메서드는 Task를 반환하고 호출자는이를 기다려야합니다.
Mrchief

1
null두 번째 매개 변수로 제공하는 것이 안전 SendAsync(...)합니까?
jocull

167

원래 질문은 .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 메서드를 사용하지 않는 것이 좋습니다.


그것을 피하는 것이 왜 더 낫습니까? 요구 사항에 따라 다릅니다.
Jowen

14
SendMailAsync ()는 어쨌든 SendAsync () 메서드를 둘러싼 래퍼입니다. async / await는 훨씬 깔끔하고 우아합니다. 정확히 동일한 요구 사항을 달성 할 것입니다.
Boris Lipschitz 2014 년

2
당신은 항상 .ContinueWith ()를 사용할 수 @RodHartzell
보리스식이 립 시즈

2
사용하거나 폐기하는 것이 더 낫습니까, 아니면 실질적인 차이가 없습니까? SendMailAsync가 실행되기 전에 smtpClient를 삭제할 수있는 마지막 '사용'블록에서 가능하지 않습니까?
niico

6
MailMessage또한 처분되어야합니다.
TheCodeKing

16

일반적으로 IDisposable 개체는 가능한 한 빨리 처리해야합니다. 개체에 IDisposable을 구현하는 것은 해당 클래스가 결정적으로 해제되어야하는 값 비싼 리소스를 보유하고 있다는 사실을 전달하기위한 것입니다. 그러나 이러한 리소스를 만드는 데 비용이 많이 들고 이러한 개체를 많이 생성해야하는 경우 하나의 인스턴스를 메모리에 유지하고 다시 사용하는 것이 더 나을 수 있습니다 (성능 측면). 그것이 차이를 만드는지 알 수있는 유일한 방법은 프로파일 링입니다!

Re : 폐기 및 비동기 : using분명히 사용할 수 없습니다 . 대신 일반적으로 SendCompleted 이벤트에서 개체를 삭제합니다.

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

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

2
보낼 각 이메일에 대해 새 SmtpClient를 만드는 것이 모범 사례입니까?
Martín Coll

1
예, 당신이 ... 콜백에서 클라이언트 처분 비동기에 대한 한, 전송
jmelhus

1
감사! 그리고 간단한 설명을 위해 : www.codefrenzy.net/2012/01/30/how-asynchronous-is-smtpclient-sendasync
Martín Coll

1
이것은 smtpclient.sendAsync 함수 및 관련 처리 처리 에 대한 stackoverflow에서 찾은 가장 간단하고 정확한 답변 중 하나입니다 . 비동기 대량 메일 발송 라이브러리를 작성했습니다. 몇 분마다 50 개 이상의 메시지를 보내므로 dispose 메서드를 실행하는 것은 저에게 매우 중요한 단계였습니다. 이 코드는 내가 그것을 달성하는 데 정확히 도움이되었습니다. 멀티 스레딩 환경에서이 코드에서 몇 가지 버그를 발견 한 경우 회신하겠습니다.
vibs2006

1
교환 서버를 구성 할 수없는 경우 (사용하는 경우) 루프에서 100 개 이상의 이메일을 보낼 때 좋은 접근 방식이 아니라고 말할 수 있습니다. 서버는 4.3.2 The maximum number of concurrent connections has exceeded a limit, closing trasmission channel. 대신에 하나 개의 인스턴스 만 사용하려고SmtpClient
ibubi

6

다음 주석을 통해 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


1
지금까지 삭제하지 않고 SMTP 클라이언트를 전송했기 때문에 예외를 공유해 주셔서 감사합니다. 자체 SMTP 서버를 사용하고 있지만 항상 좋은 프로그래밍 관행을 고려해야합니다. 귀하의 오류를 살펴보면 이제주의를 받았으며 플랫폼 안정성을 보장하기 위해 처리 기능을 포함하도록 코드를 수정합니다.
vibs2006
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.