在SendCompleted事件中重新发送邮件

本文关键字:新发送 SendCompleted 事件 | 更新日期: 2023-09-27 18:12:33

我有一个名为scheduleservice的类,其中我有一个SendMail函数,当需要在特定时间发送电子邮件时调用该函数。当我调用SendMail函数时,我传入一个对象,该对象包含发送电子邮件的对象和发件人的信息。现在,我已经添加了一个SendCompleted Handler,这样我就可以重新发送电子邮件,以防发生某些事情导致邮件无法发送。我有这个发送邮件的代码:

    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字段是空的,这会导致发送电子邮件时出现错误。我应该如何从发送失败的电子邮件中回收To/From字段?

在SendCompleted事件中重新发送邮件

如果你想使用异步发送,那么你应该摆脱using块,它会处置你初始化的message变量。相反,你可以从SendCompleted eventandler调用Dispose(),或者从初始化方法调用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,需要先设置执行操作所需的其他参数。

乌利希期刊指南它将解决处理原始message变量的问题,因为这就是为什么"To和From字段是空的"。

关于重新发送-请提供更多的代码或情况的例子,无论如何和在哪里,你要避免消息发送取消和重新发送。

另外,在reending of cancelled message下面到底是什么意思?您想再次发送取消消息,对吗?据我所知,在您的情况下,您没有可能继续发送使用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方法测试了它,所以我希望取消原因的最大部分被考虑在内。

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

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

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

一种不会出错的方法是创建一个类似于这样的循环:
  • 设置"邮件发送成功"标志为false
  • 设置"发送"标志为true
  • 尝试异步发送邮件
  • 在您正在处理的事件中,检查e.Canceled -如果为false,将"主发送成功"标志设置为true。同时将"发送"标志设置为false(您已完成发送过程,对吗?)
  • 暂时做点别的事情。如果没有其他可用的,做Application.DoEvents()(是的,它在很多层面上都是错误的,但现在让我们保持简单)
  • 检查"发送"标志。如果仍然设置,重复前面的步骤一段时间。使用计数器不会永远卡住。
  • 检查"邮件已成功发送"标志。如果未设置,请重复。减少一些计数器,所以你不应该永远这样做。

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

SendAsync方法的第二个参数接受一个用户令牌,该令牌可以从e.s userstate中读取。所以把你的SendAsync代码改成:

    smtpClient.SendAsync(message, message);

和你的代码将正常工作。

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