C# 在SendCompleted事件中重新发送电子邮件

C# 在SendCompleted事件中重新发送电子邮件,c#,C#,我有一个名为SchedulerService的类,其中有一个SendMail函数,当需要在某个时间发送电子邮件时调用该函数。当我调用SendMail函数时,我会传入一个对象,该对象包含向谁发送电子邮件以及电子邮件来自谁的信息。现在,我添加了一个SendCompleted处理程序,这样我就可以重新发送电子邮件,以防发生导致它无法发送的情况。我有发送邮件的代码: var recipients = EmailTo.Split(',').ToList(); if (String.IsNu

我有一个名为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);
        }

    }

问题是,我发现这样做行不通,因为“收件人”和“发件人”字段为空,这会导致发送电子邮件时出错。我应该如何从发送失败的电子邮件中回收“收件人/发件人”字段

如果希望使用异步发送,那么应该使用块摆脱
,它将处理初始化的
消息
变量。相反,您可以从SendCompleted eventHandler调用
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);
也就是说,我的意思是,应该首先设置
sendaync
Credentails
以及执行操作所需的其他参数

UPD 它将解决处理原始
消息
变量的问题,因为这就是“收件人和发件人字段为空”的原因

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

另外,在重新发送已取消消息的情况下,您的意思是什么?您是否希望再次发送取消的邮件?据我所知,您不可能继续发送在您的情况下使用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方法对其进行了测试,所以我希望取消原因的大部分都被考虑在内

如果希望使用异步发送,那么应该使用
块摆脱
,它将处理初始化的
消息
变量。相反,您可以从SendCompleted eventHandler调用
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);
也就是说,我的意思是,应该首先设置
sendaync
Credentails
以及执行操作所需的其他参数

UPD 它将解决处理原始
消息
变量的问题,因为这就是“收件人和发件人字段为空”的原因

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

另外,在重新发送已取消消息的情况下,您的意思是什么?您是否希望再次发送取消的邮件?据我所知,您不可能继续发送在您的情况下使用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);
}

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

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.cancelled
    -如果为false,则将“main send successfully”标志设置为true。还将“发送”标志设置为false(您完成了发送过程,对吗?)
  • 在一段时间内做些别的事情。如果没有其他可用的,请执行
    Application.DoEvents()
    (是的,在很多级别上都是错误的,但现在让我们保持简单)
  • 检查“发送”标志。如果仍然设置,则重复上一步骤一段时间。使用计数器不要永远卡住
  • 检查“邮件发送成功”标志。如果未设置,请重复。减少一些计数器,所以你不应该永远这样做

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

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

避免出错的一种方法是创建类似以下内容的循环:

  • 将“邮件发送成功”标志设置为false
  • 设置