在SendCompleted Event中重新发送电子邮件

时间:2013-05-08 13:37:08

标签: c#

我有一个名为SchedulerService的类,其中有一个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字段为空,然后在发送电子邮件时导致错误。我该如何从无法发送的电子邮件中回收“收件人”/“收件人”字段?

4 个答案:

答案 0 :(得分:2)

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

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

您也可以在this page 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);

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

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

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

在完全取消消息的重新发送时,你的意思是什么?你想再次发送被取消的信息是对的吗?据我所知,您无法继续发送在您的情况下使用SendAsyncCancel()取消的消息。

如果执行了SendAsyncCancel,它仍然会引发SendCompleted事件,但是传递的参数表明操作已被取消。所以,你无法摆脱它。您可能需要查看this page,当然还有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方法测试它,所以我希望考虑到取消原因的大部分。

答案 1 :(得分: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);
}

答案 2 :(得分:1)

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

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

  • 将'mail sent successfully'标志设置为false
  • 将'发送'标志设置为true
  • 尝试以异步方式发送邮件
  • 如果您正在处理,请检查e.Canceled - 如果为false,请将'main send successful'标志设置为true。同时将'sending'标志设置为false(你完成了发送过程,对吗?)
  • 做一些其他的事情。如果没有别的可用,请Application.DoEvents()(是的,这么多级别是错的,但现在让我们保持简单)
  • 检查'发送'标志。如果仍然设置,请重复上一步骤一段时间。使用计数器不会永远卡住。
  • 检查'邮件发送成功'标志。如果未设置,请重复。减少一些计数器,所以你不应该永远这样做。

为了更好的衡量,将上述所有内容放在次要线程中。而不是DoEvents()只是Sleep(50)或类似的东西,所以你不会吃CPU时间。

答案 3 :(得分:0)

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

    smtpClient.SendAsync(message, message);

您的代码将正常运行。

同样在SendComplete事件中,您可以输入发送者来获取实际的SmtpClient。