1

我有一个名为 SchedulerService 的类,其中有一个 SendMail 函数,当需要在某个时间发送电子邮件时调用该函数。当我调用 SendMail 函数时,我传入一个对象,该对象包含有关向谁发送电子邮件以及电子邮件来自谁的信息。现在,我添加了一个 SendCompleted 处理程序,以便我可以重新发送电子邮件,以防万一发生导致它无法发送的情况。我有这个发送邮件的代码:

    var recipients = EmailTo.Split(',').ToList();
    if (String.IsNullOrEmpty(EmailFrom))
        EmailFrom = recipients[0];

    using (MailMessage message = new MailMessage())
      {
        message.From = new MailAddress(EmailFrom);
        recipients.ForEach(a => message.To.Add(new MailAddress(a)));
        message.Attachments.Add(new Attachment(LocationOfResults));
        message.Subject = String.Format("{0:MM-dd-yyyy} Results for task: {1}.", DateTime.Now, Description);
        message.Body = "Attached is the results file specified for the task: " + Description;
        smtpClient.SendCompleted += new SendCompletedEventHandler(SendCompletedCallback);
        smtpClient.UseDefaultCredentials = true;
        smtpClient.SendAsync(message, null);

                }

这是事件处理程序

    private void SendCompletedCallback(object sender, AsyncCompletedEventArgs e)
    {
        if (e.Cancelled)
        {
            MailMessage mail = (MailMessage)e.UserState;
            using (mail)
            {
                 smtpClient.Send(mail);
            }
        }
        if (e.Error != null)
        {
            Log(e.Error.ToString() + " in SendCompletedHandlerEvent", EventLogEntryType.Error);
        }

    }

问题是,我发现这样做不起作用,因为 To 和 From 字段是空的,这会在发送电子邮件时导致错误。我应该如何从未能发送的电子邮件中回收收件人/发件人字段?

4

4 回答 4

2

如果你想使用异步发送,那么你应该摆脱using阻塞,它会处理你初始化的message变量。相反,您可以Dispose()从 SendCompleted eventHandler 调用或仅从初始化方法调用 Dispose:

 /*...*/
 smtpClient.SendAsync(message, null);
 message.Dispose();

您也可以在此页面MSDN上找到这种方法

此外,您应该以某种方式重写您的初始化,使其看起来像这样:

smtpClient.Credentials = new NetworkCredential("somemail@gmail.com", "pass");
            smtpClient.Port = 587;
            smtpClient.Host = "smtp.gmail.com";
            smtpClient.SendAsync(message, null);
            smtpClient.SendCompleted  += new SendCompletedEventHandler(smtpClient_SendCompleted);

也就是说,我的意思是应该首先设置执行操作所需的其他参数SendAsyncCredentails

UPD 它将解决处理原始message变量的问题,因为这就是“To 和 From 字段为空”的原因。

关于重新发送 - 请提供更多代码或情况示例,无论您将如何以及在何处避免消息发送取消并重新发送。

另外,在重新发送已取消消息的情况下,您是什么意思?您想再次发送已取消的消息是否正确?据我了解,在您的情况下,您无法继续发送使用 SendAsyncCancel() 取消的消息。

在执行 SendAsyncCancel 的情况下,它仍会引发 SendCompleted 事件,但其传递的参数表明操作已取消。所以,你只是无法逃脱它。您可能想查看此页面,当然还有 MSDN。

如果您需要发送这么多消息以防出现取消发送的问题,只需再次发送即可:

  /*your SendCompleted EventHandler*/
 if (e.Canceled)
  {
    //if you use using(message) {...} here, you'll get ObjectDisposedException again
   SmtpClient smtp;
    smtp = new SmtpClient();  
     smtp = GetClient(smtp);  //method of your smtpClient initialization
    MailMessage mess = new MailMessage();
    mess = GetMessage(mess, smtp);  //method of your mailMessage initialization

    try
    {
        //sending message again
        smtp.SendAsync(mess, null);

    }
    catch (ObjectDisposedException e)
    {
       MessageBox.Show("The email message was not sent. See the details:\n"+e.Message,
      "Error sendiing message")

     }
  }

我使用 SendAsyncCancel 方法对其进行了测试,因此我希望考虑到大部分取消原因。

于 2013-05-08T14:23:41.640 回答
1

您似乎正在将与同步场景相关的代码与特定于异步场景的代码混合在一起。因此,从异步的角度来看,这是一个基本示例,说明如何安全地首先发送电子邮件:

var recipients = EmailTo.Split(',').ToList();
if (String.IsNullOrEmpty(EmailFrom))
    EmailFrom = recipients[0];

MailMessage message = new MailMessage()
{
    From = new MailAddress(EmailFrom),
    Subject = String.Format("{0:MM-dd-yyyy} Results for task: {1}.", DateTime.Now, Description),
    Body = "Attached is the results file specified for the task: " + Description;
};
recipients.ForEach(a => message.To.Add(new MailAddress(a)));
message.Attachments.Add(new Attachment(LocationOfResults));
smtpClient.SendCompleted += new SendCompletedEventHandler(SendCompletedCallback);
smtpClient.UseDefaultCredentials = true;
smtpClient.SendAsync(message, message); // IMPORTANT - send message as UserState so we can access it in the callback

如何处理回调和处理消息:

MailMessage msg = (MailMessage)e.UserState;
if (e.Cancelled)
{
    // force synchronous send
    smptClient.Send(msg);
}
msg.Dispose(); // dispose of the message as we no longer need it
if (e.Error != null)
{
    Log(e.Error.ToString() + " in SendCompletedHandlerEvent", EventLogEntryType.Error);
}
于 2013-05-09T14:02:21.197 回答
1

正如我在评论中所说,你做错了。

一种不做错的方法是创建一个类似这样的循环:

  • 将“邮件发送成功”标志设置为 false
  • 将“发送”标志设置为真
  • 尝试异步发送邮件
  • 如果您正在处理,请检查e.Canceled- 如果为假,请将“主要发送成功”标志设置为真。还将“发送”标志设置为 false(您完成了发送过程,对吗?)
  • 做一些别的事情一段时间。如果没有其他可用的,请执行Application.DoEvents()(是的,在很多层面上都是错误的,但现在让我们保持简单)
  • 检查“发送”标志。如果仍然设置,请重复上一步一段时间。使用计数器不会永远卡住。
  • 检查“邮件发送成功”标志。如果未设置,请重复。减少一些计数器,所以你不应该永远这样做。

为了更好地衡量,将上面所说的所有内容放在辅助线程中。而不是DoEvents()仅仅Sleep(50)或类似的东西,这样你就不会占用 CPU 时间。

于 2013-05-09T17:08:06.203 回答
0

SendAsync 方法的第二个参数采用可从 e.UserState 读取的用户令牌。因此,将您的 SendAsync 代码更改为:

    smtpClient.SendAsync(message, message);

您的代码将正常工作。

同样在 SendComplete 事件中,您可以对 sender 进行类型转换以获取实际的 SmtpClient。

于 2014-04-09T14:49:52.253 回答