在C#中异步发送电子邮件?

时间:2010-08-04 18:05:57

标签: c# visual-studio email asynchronous

我正在开发一个应用程序,其中用户点击/按下窗口中某个按钮的输入,应用程序执行一些检查并确定是否发送几封电子邮件,然后显示另一个带有消息的窗口。

我的问题是,发送2封电子邮件会显着减慢这个过程,并且在某些(约8)秒内,第一个窗口会在发送时冻结。

我有什么方法可以在后台发送这些电子邮件并立即显示下一个窗口?

请不要使用“使用X类”或“仅使用X方法”来限制您的答案,因为我还不太熟悉该语言,并且会非常感谢更多信息。

感谢。

10 个答案:

答案 0 :(得分:68)

从.NET 4.5开始,SmtpClient实现异步等待方法 SendMailAsync。 因此,异步发送电子邮件如下:

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

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

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

答案 1 :(得分:24)

由于它是一个小工作单元,您应该使用ThreadPool.QueueUserWorkItem来处理它的线程方面。如果您使用SmtpClient课程发送邮件,则可以处理SendCompleted事件以向用户提供反馈。

ThreadPool.QueueUserWorkItem(t =>
{
    SmtpClient client = new SmtpClient("MyMailServer");
    MailAddress from = new MailAddress("me@mydomain.com", "My Name", System.Text.Encoding.UTF8);
    MailAddress to = new MailAddress("someone@theirdomain.com");
    MailMessage message = new MailMessage(from, to);
    message.Body = "The message I want to send.";
    message.BodyEncoding =  System.Text.Encoding.UTF8;
    message.Subject = "The subject of the email";
    message.SubjectEncoding = System.Text.Encoding.UTF8;
    // Set the method that is called back when the send operation ends.
    client.SendCompleted += new SendCompletedEventHandler(SendCompletedCallback);
    // The userState can be any object that allows your callback 
    // method to identify this send operation.
    // For this example, I am passing the message itself
    client.SendAsync(message, message);
});

private static void SendCompletedCallback(object sender, AsyncCompletedEventArgs e)
{
        // Get the message we sent
        MailMessage msg = (MailMessage)e.UserState;

        if (e.Cancelled)
        {
            // prompt user with "send cancelled" message 
        }
        if (e.Error != null)
        {
            // prompt user with error message 
        }
        else
        {
            // prompt user with message sent!
            // as we have the message object we can also display who the message
            // was sent to etc 
        }

        // finally dispose of the message
        if (msg != null)
            msg.Dispose();
}

通过每次创建一个新的SMTP客户端,您可以同时发送电子邮件。

答案 2 :(得分:11)

简单地在单独的线程上发送消息并不复杂:

using System.Net.Mail;

Smtp.SendAsync(message);

或者,如果你想在单独的线程上构造整个消息而不是仅仅异步发送它:

using System.Threading;
using System.Net.Mail;

var sendMailThread = new Thread(() => {
    var message=new MailMessage();
    message.From="from e-mail";
    message.To="to e-mail";
    message.Subject="Message Subject";
    message.Body="Message Body";

    SmtpMail.SmtpServer="SMTP Server Address";
    SmtpMail.Send(message);
});

sendMailThread.Start();

答案 3 :(得分:8)

SmtpClient.SendAsync Method

样品

using System;
using System.Net;
using System.Net.Mail;
using System.Net.Mime;
using System.Threading;
using System.ComponentModel;
namespace Examples.SmptExamples.Async
{
    public class SimpleAsynchronousExample
    {
        static bool mailSent = false;
        private static void SendCompletedCallback(object sender, AsyncCompletedEventArgs e)
        {
            // Get the unique identifier for this asynchronous operation.
             String token = (string) e.UserState;

            if (e.Cancelled)
            {
                 Console.WriteLine("[{0}] Send canceled.", token);
            }
            if (e.Error != null)
            {
                 Console.WriteLine("[{0}] {1}", token, e.Error.ToString());
            } else
            {
                Console.WriteLine("Message sent.");
            }
            mailSent = true;
        }
        public static void Main(string[] args)
        {
            // Command line argument must the the SMTP host.
            SmtpClient client = new SmtpClient(args[0]);
            // Specify the e-mail sender. 
            // Create a mailing address that includes a UTF8 character 
            // in the display name.
            MailAddress from = new MailAddress("jane@contoso.com", 
               "Jane " + (char)0xD8+ " Clayton", 
            System.Text.Encoding.UTF8);
            // Set destinations for the e-mail message.
            MailAddress to = new MailAddress("ben@contoso.com");
            // Specify the message content.
            MailMessage message = new MailMessage(from, to);
            message.Body = "This is a test e-mail message sent by an application. ";
            // Include some non-ASCII characters in body and subject. 
            string someArrows = new string(new char[] {'\u2190', '\u2191', '\u2192', '\u2193'});
            message.Body += Environment.NewLine + someArrows;
            message.BodyEncoding =  System.Text.Encoding.UTF8;
            message.Subject = "test message 1" + someArrows;
            message.SubjectEncoding = System.Text.Encoding.UTF8;
            // Set the method that is called back when the send operation ends.
            client.SendCompleted += new 
            SendCompletedEventHandler(SendCompletedCallback);
            // The userState can be any object that allows your callback  
            // method to identify this send operation. 
            // For this example, the userToken is a string constant. 
            string userState = "test message1";
            client.SendAsync(message, userState);
            Console.WriteLine("Sending message... press c to cancel mail. Press any other key to exit.");
            string answer = Console.ReadLine();
            // If the user canceled the send, and mail hasn't been sent yet, 
            // then cancel the pending operation. 
            if (answer.StartsWith("c") && mailSent == false)
            {
                client.SendAsyncCancel();
            }
            // Clean up.
            message.Dispose();
            Console.WriteLine("Goodbye.");
        }
    }
}

答案 4 :(得分:5)

仅仅因为这有点模糊......我会简短......

在c#/ .net等中有很多方法可以进行异步或并行工作。

执行所需操作的最快方法是使用后台工作线程,这样可以避免锁定UI。

有后台工作线程的提示:你不能直接从它们更新UI(线程亲和力和编组只是你学会处理的东西......)

另一件需要考虑的事情......如果你使用标准的System.Net.Mail类型的东西来发送电子邮件......要小心你如何制作你的逻辑。如果你在某种方法中将它全部隔离并反复调用它,那么每次都可能必须拆除并重建与邮件服务器的连接,并且认证等中涉及的延迟仍然会使整个事情不必要地减慢。在可能的情况下,通过单个打开的连接向邮件服务器发送多封电子邮件。

答案 5 :(得分:5)

以下是使用.Net 4.5.2 +的 fire and forget方法和async

BackgroundTaskRunner.FireAndForgetTaskAsync(async () =>
{
    SmtpClient smtpClient = new SmtpClient(); // using configuration file settings
    MailMessage message = new MailMessage(); // TODO: Initialize appropriately
    await smtpClient.SendMailAsync(message);
});

BackgroundTaskRunner是:

public static class BackgroundTaskRunner
{     
    public static void FireAndForgetTask(Action action)
    {
        HostingEnvironment.QueueBackgroundWorkItem(cancellationToken => // .Net 4.5.2+ required
        {
            try
            {
                action();
            }
            catch (Exception e)
            {
                // TODO: handle exception
            }
        });
    }

    /// <summary>
    /// Using async
    /// </summary>
    public static void FireAndForgetTaskAsync(Func<Task> action)
    {
        HostingEnvironment.QueueBackgroundWorkItem(async cancellationToken => // .Net 4.5.2+ required
        {
            try
            {
                await action();
            }
            catch (Exception e)
            {
                // TODO: handle exception
            }
        });
    }
}

就像Azure App Services上的魅力一样。

答案 6 :(得分:4)

试试这个:

var client = new System.Net.Mail.SmtpClient("smtp.server");
var message = new System.Net.Mail.MailMessage() { /* provide its properties */ };
client.SendAsync(message, null);

答案 7 :(得分:2)

使用SmtpClient类并使用System.Net.Mail命名空间中的方法SendAsync

答案 8 :(得分:1)

您要做的是在单独的线程上运行电子邮件任务,以便主代码可以继续处理,而另一个线程可以处理电子邮件。

这是一个如何做到这一点的教程: Threading Tutorial C#

答案 9 :(得分:1)

使用.NET 4.0中的Task Parallel Library,您可以执行以下操作:

Parllel.Invoke(() => { YourSendMailMethod(); });

另外,请参阅有关Parallel.Invoke()与显式任务管理的cristina manu's博文。