Search code examples
c#async-awaitsmtpclient

C# sending multiple emails asynchronously with cancellation token


I am trying to send multiple messages using SendMailAsync and I am trying to pass a cancellation token to interrupt the sending process when the cancellation has been requested.

static void Send(SmtpClientFactory factory, IEnumerable<MailMessage> messages)
{
    Task.WaitAll(SendAsync(factory, messages, CancellationToken.None));
}

static async Task SendAsync(SmtpClientFactory factory, IEnumerable<MailMessage> messages, CancellationToken token)
{
    await Task.Run(async () =>
    {
        using (SmtpClient smtpClient = factory())
        {
            foreach (MailMessage message in messages)
            {
                await smtpClient.SendMailAsync(message).ConfigureAwait(false);
            }
        }
    }, token);
}

When I use the non-async Send, it gets into a deadlock. How can I send my message synchrnously and use a cancellation token to cancel when the time is up?


Solution

  • You can combine CancellationToken.Register and SmtpClient.SendAsyncCancel to do what you need:

    static async Task SendAsync(SmtpClientFactory factory, IEnumerable<MailMessage> messages, CancellationToken token)
    {
        await Task.Run(async () =>
        {
            using (SmtpClient smtpClient = factory())
            {
                foreach (MailMessage message in messages)
                {
                    token.ThrowIfCancellationRequested();
    
                    using (token.Register(() => smtpClient.SendAsyncCancel()))
                    {
                        await smtpClient.SendMailAsync(message).ConfigureAwait(false);
                    }
                }
            }
        }, token);
    }
    

    Keep in mind that SmtpClient is officially deprecated:

    SmtpClient and its network of types are poorly designed, we strongly recommend you use https://github.com/jstedfast/MailKit and https://github.com/jstedfast/MimeKit instead

    Not only is MailKit threadsafe, but it's far more flexible in terms of message construction, and it supports CancellationToken to boot!