这是真正的异步电子邮件和最佳做法吗?

时间:2022-09-17 02:17:05

I am trying to learn and master ASYNC and AWAIT but it is not clear if what I do is the right thing at then end.. I mean the code works and I took two browsers/instances of the same form and click, well, almost at the same time and everything went OK. I received 2 emails but is it the right way to do it? and does it really gives a plus value to do it async instead of sync...

我正在努力学习和掌握ASYNC和AWAIT,但目前尚不清楚我的工作是否正确到底。我的意思是代码工作,我拿了两个相同形式的浏览器/实例并点击,好吧,几乎在同一时间,一切都很好。我收到了2封电子邮件,但这是正确的方式吗?它真的给了一个加号值来做异步而不是同步...

This is my code:

这是我的代码:

    [HttpPost]
    [AllowAnonymous]
    [ValidateAntiForgeryToken]
    public async Task<ActionResult> Register(RegisterViewModel model)
    {
       bla bla do some sync simple stuffs etc...
       ...
       await Task.Run(() => Utility.MailUtility.SendEmail(user.Email, user.To, msg, subj));
       return RedirectToAction("Index", "Home");
    }

and then the mailing code looks like this, as per a sample code taken from *:

然后邮件代码看起来像这样,根据从*获取的示例代码:

    public static async Task SendEmail(params etc...) 
    {
        await SendMailForTemplateID(params etc...);
    }

    private static Task SendMailForTemplateID(params etc...)
    {


            var task = Task.Factory.StartNew(() =>
            {
                MailMessage message = new MailMessage();
                MailMessage mail = new MailMessage();
                //SmtpClient SmtpServer = new SmtpClient(ConfigurationManager.AppSettings["SMTPHost"]);
                SmtpClient SmtpServer = new SmtpClient("localhost"); // for test purpose only
                mail.From = new MailAddress(from);
                mail.To.Add(to);
                mail.Subject = GetSubjectForTemplateId(templateID, extraMsg, dataInfo, fromName, toName, isFr);
                mail.Body = GetBodyForTemplateId(templateID, extraMsg, dataInfo, fromName, toName, isFr);
                mail.IsBodyHtml = false; 
                SmtpServer.Port = 81; // for test purpose only
                SmtpServer.Credentials = new System.Net.NetworkCredential
                    (ConfigurationManager.AppSettings["CredentialUser"],
                    ConfigurationManager.AppSettings["CredentialPassword"]);

                bool result;
                bool.TryParse(ConfigurationManager.AppSettings["EnableSSL"], out result);
                SmtpServer.EnableSsl = result;

                SmtpServer.Send(mail);

                //* make sure we leave nothing in memory
                message.Dispose();
                mail.Dispose();
                SmtpServer.Dispose();


            });

            return task;

       }

I am sorry guys but I don't know how to put line return (enter) between my replies. Is It possible? nway, regarding ASYNC-EMAIL best practice.

我很抱歉,但我不知道如何在我的回复之间放回线(回车)。可能吗?不,关于ASYNC-EMAIL的最佳实践。

My new version of code looks like this, it works more simple coding but is it right? I mean no new threads, does it give a plus value VS sync sendmail methods etc...

我的新版本的代码看起来像这样,它的编码更简单,但是它是对的吗?我的意思是没有新的线程,它是否给出了加值VS sync sendmail方法等...

I call it in a MVC action. The code:

我把它称为MVC动作。代码:

     do stuff...
     await Utility.MailUtility.SendMailForTemplateID(params);
     do stuff...

   public static async Task SendMailForTemplateID(params...)
    {
        try
        {
            email code etc...

            await smtpClient.SendMailAsync(mail);

            mail.Dispose();
            smtpClient.Dispose();


        }
        catch (Exception)
        {
            // Log ex
        }
    }

1 个解决方案

#1


4  

No, wrapping async calls into Task.Run is wrong way of writing async/await code.

不,将异步调用包装到Task.Run是编写异步/等待代码的错误方法。

One particular problem with your current code is Task.Run and Task.Factory.StartNew start new threads to execute operation - so code starts 2 extra threads to execute operation.

当前代码的一个特殊问题是Task.Run和Task.Factory.StartNew启动新线程来执行操作 - 因此代码启动2个额外的线程来执行操作。

Correct way - remove all Task.Run/Task.Factory.StartNew calls and await asynchronous calls all the way down. In your case SmtpClient.SendMailAsync should be used at lowest level instead of SmtpServer.Send(mail);.

正确的方法 - 删除所有Task.Run/Task.Factory.StartNew调用并等待异步调用。在你的情况下,应该在最低级别使用SmtpClient.SendMailAsync而不是SmtpServer.Send(mail);.

In more generic case when there is no Task-based API but there is some alternative asynchronous APIs (like event-based SmtpClient.SendAsync ) you may need to provide wrapping helper methods to transform such calls to Task-based async methods. Some approaches shown in Convert Event based pattern to async CTP pattern

在更通用的情况下,当没有基于任务的API但是有一些替代异步API(如基于事件的SmtpClient.SendAsync)时,您可能需要提供包装辅助方法来将此类调用转换为基于任务的异步方法。将基于事件的模式转换为异步CTP模式中显示的一些方法


Approximate code:

近似代码:

public async Task<ActionResult> Register(RegisterViewModel model)
{
   ...
   await Utility.MailUtility.SendEmailAsync(user.Email, user.To, msg, subj));
   return RedirectToAction("Index", "Home");
}

// No need to add `async` here as there is no `await` inside,
// possibly can be removed altogether.
public static Task SendEmailAsync(params etc...) 
{
    return SendMailForTemplateID(params etc...);
}

private async static Task SendMailForTemplateID(params etc...)
{
     // not Task.Run / Task.Factory.StartNew

     using (MailMessage message = ... )
     using (SmtpClient smtp = ....)
     {
            .... // setup message/smtp

            await smtp.SendMailAsync(mail); // Task-based Send
     }
     // no need to return anything for `Task` (essentially `void`) method 
   } 

#1


4  

No, wrapping async calls into Task.Run is wrong way of writing async/await code.

不,将异步调用包装到Task.Run是编写异步/等待代码的错误方法。

One particular problem with your current code is Task.Run and Task.Factory.StartNew start new threads to execute operation - so code starts 2 extra threads to execute operation.

当前代码的一个特殊问题是Task.Run和Task.Factory.StartNew启动新线程来执行操作 - 因此代码启动2个额外的线程来执行操作。

Correct way - remove all Task.Run/Task.Factory.StartNew calls and await asynchronous calls all the way down. In your case SmtpClient.SendMailAsync should be used at lowest level instead of SmtpServer.Send(mail);.

正确的方法 - 删除所有Task.Run/Task.Factory.StartNew调用并等待异步调用。在你的情况下,应该在最低级别使用SmtpClient.SendMailAsync而不是SmtpServer.Send(mail);.

In more generic case when there is no Task-based API but there is some alternative asynchronous APIs (like event-based SmtpClient.SendAsync ) you may need to provide wrapping helper methods to transform such calls to Task-based async methods. Some approaches shown in Convert Event based pattern to async CTP pattern

在更通用的情况下,当没有基于任务的API但是有一些替代异步API(如基于事件的SmtpClient.SendAsync)时,您可能需要提供包装辅助方法来将此类调用转换为基于任务的异步方法。将基于事件的模式转换为异步CTP模式中显示的一些方法


Approximate code:

近似代码:

public async Task<ActionResult> Register(RegisterViewModel model)
{
   ...
   await Utility.MailUtility.SendEmailAsync(user.Email, user.To, msg, subj));
   return RedirectToAction("Index", "Home");
}

// No need to add `async` here as there is no `await` inside,
// possibly can be removed altogether.
public static Task SendEmailAsync(params etc...) 
{
    return SendMailForTemplateID(params etc...);
}

private async static Task SendMailForTemplateID(params etc...)
{
     // not Task.Run / Task.Factory.StartNew

     using (MailMessage message = ... )
     using (SmtpClient smtp = ....)
     {
            .... // setup message/smtp

            await smtp.SendMailAsync(mail); // Task-based Send
     }
     // no need to return anything for `Task` (essentially `void`) method 
   }