简体   繁体   English

异步触发并忘记

[英]Fire and forget with async

Consider this code: 考虑以下代码:

public async Task<Status> SendMessage(Message message)
{
    List<IMessage> _messageDispatchers = new List<IMessage>();
    try
    {
        Object[] args = new Object[] { _message };
        IMessage endpoint = (IMessage)Activator.CreateInstance(Type.GetType(_message.AgentDLLName), args);
        _messageDispatchers.Add(endpoint);

        foreach (IMessage dispatcher in _messageDispatchers)
        {
            await Task.Run(() => dispatcher.SendMessage(_message));
        }
        return await Task.Run(() => Status.Success);
    }
    catch (Exception ex)
    {
        logger.Log(LoggerLevel.Error, ex.Message);
        return Status.EmailSendingFailed;
    }

}

the SendMessage: SendMessage:

public async Task<Status> SendMessage(OutboundMessage outboundmessage)
{
    string strMessage = string.Empty;
    string subject = string.Empty;
    MessageServices objService = new MessageServices();
    try
    {
        var config = (from SmtpConfigurationElement ms in AppConfiguration.Instance.Smtps
                      where ms.Key == "smtp"
                      select ms).Single();

        SmtpClient smtpClient = new SmtpClient(config.Host);
        smtpClient.Port = Convert.ToInt32(config.port);
        smtpClient.EnableSsl = true;
        smtpClient.Credentials = new NetworkCredential(config.UserName, config.Password);

        string[] strToList = outboundmessage.ToList.Split(';');
        MailMessage mail = new MailMessage();
        mail.From = new MailAddress(outboundmessage.FromAddress);

        if (strToList.Length > 0)
        {
            for (int j = 0; j < strToList.Length; j++)
            {
                mail.To.Add(strToList[j]);
            }
        }
        else
        {
            _LOGGER.Log(LoggerLevel.Information, "SMTP Mail Send failed as ToList is not correct");
            return Status.Failed;
        }

        if (!string.IsNullOrEmpty(outboundmessage.CCList))
        {
            string[] strCCList = outboundmessage.CCList.Split(';');
            if (strCCList.Length > 0)
            {
                for (int k = 0; k < strCCList.Length; k++)
                {
                    mail.CC.Add(strToList[k]);
                }
            }
        }

        if (!string.IsNullOrEmpty(outboundmessage.Attachments))
        {
            System.Net.Mail.Attachment attachment;
            attachment = new System.Net.Mail.Attachment(outboundmessage.Attachments);
            mail.Attachments.Add(attachment);
        }

        strMessage = await objService.ReplaceMessageWithPlaceholders(outboundmessage.PlaceholderValues, outboundmessage.MessageBody);
        subject = await objService.ReplaceMessageWithPlaceholders(outboundmessage.PlaceholderValues, outboundmessage.Subject);
        mail.Body = strMessage;
        mail.Subject = subject;
        mail.IsBodyHtml = true;
        await Task.Run(() => smtpClient.Send(mail));


        return Status.Success;
    }
    catch (Exception ex)
    {
        return Status.Failed;
    }
}

And the call to SendMessage: 并调用SendMessage:

public Status MarketingEmail(OutboundMessage _message)
{
    try
    {
        _message.MessageCreatedDate = System.DateTime.Now;
        processor.SendMessage(_message);
        return Status.Success;
    }
    catch (Exception ex)
    {
        _LOGGER.Log(LoggerLevel.Error, "Error in Marketing Email" + ex.ToString());
        return Status.InsertFailed;
    }
}

The whole idea is to make a workflow in which sending of the email is the last task and that should be a fire and forget thing. 整个想法是建立一个工作流,其中电子邮件的发送是最后的任务,应该是一劳永逸的事情。

Now the call to processor.SendMessage(_message) has a suggestion like this: 现在,对processor.SendMessage(_message)的调用具有如下建议:

Because this call is not awaited, execution of the current method continues before the call is completed. 因为不等待此调用,所以在调用完成之前将继续执行当前方法。 Consider applying the 'await' operator to the result of the call. 考虑将“ await”运算符应用于调用结果。

Which is a valid thing since async & await need to be used together. 这是有效的事情,因为异步和等待需要一起使用。

Questions: 问题:

  1. Will the current approach work without any trouble if the suggestion is ignored? 如果忽略该建议,当前方法是否可以正常工作? (I am asking this since this is still in the development stage and I can make the suggested design changes now rather than face any critical issues later.) (我之所以这样问,是因为它仍处于开发阶段,我现在可以进行建议的设计更改,而不必以后再遇到任何关键问题。)
  2. What is the suggested best practice to design a workflow considering the said requirement? 考虑到上述要求,设计工作流程的最佳建议是什么?

The current approach will "work" in the sense that it will continue on to return Status.Success; 当前的方法将在继续return Status.Success;的意义上“起作用” return Status.Success; without waiting for the call to processor.SendMessage(_message); 无需等待对processor.SendMessage(_message);的调用processor.SendMessage(_message); to complete. 去完成。

However, since that call was fired & forgotten, and that SendMessage overload doesn't do any logging in the catch block, you run the risk of emails failing to be sent but nobody getting notified about it. 但是,由于该呼叫已被触发并被遗忘,并且SendMessage重载未在catch块中进行任何记录,因此冒着电子邮件发送失败但没有人收到通知的风险。

A common approach for async email sending is this: Stash the email somewhere else (typically a message queue or a database), and then set up a separate async process that reads the queued emails and sends them. 异步电子邮件发送的一种常见方法是:将电子邮件存储在其他地方(通常是消息队列或数据库),然后设置一个单独的异步过程,该过程读​​取排队的电子邮件并发送。 If it succeeds, it flags the email as sent. 如果成功,则将电子邮件标记为已发送。 If it fails, it tries again (up to a certain time limit or # of retries), and then if it gives up, it can trigger a notification or set a flag that can be checked later. 如果失败,它将再次尝试(达到一定的时间限制或重试次数),然后放弃,它可以触发通知或设置一个标志,以便以后检查。

Then your code will basically be saying "okay, the email was successfully queued", instead of "okay, the email was sent". 然后您的代码将基本上说“好,电子邮件已成功排队”,而不是“好,电子邮件已发送”。 Moving the actual sending to a separate process is much more reliable. 将实际的发送转移到一个单独的过程要可靠得多。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM