【问题标题】:How to call Async Method within Task.Run? [duplicate]如何在 Task.Run 中调用异步方法? [复制]
【发布时间】:2016-12-28 13:12:00
【问题描述】:

我需要以异步方式发送邮件。我已经想出使用 Razor Generator 从剃刀视图生成 Html 模板。现在我需要使用 SmtpClient.SendMailAsync 将 html 作为邮件发送。但我发现 Razor 生成器需要相当长的时间,我不想在我的发送邮件方法中包含模板生成部分,因为发送邮件方法不应该关心获取 Html 模板。

我有示例代码:

public static void SendEmailAsync<TModel>(TModel model, string templatePath, string subj, string toEmail, string cc = null, string bcc = null)
    {

        string templateFilePath = HostingEnvironment.MapPath(templatePath);
        // Generate the email body from the template file.
        // 'templateFilePath' should contain the absolute path of your template file.
        if (templateFilePath != null)
        {
            Task.Run(() =>
            {
                var emailHtmlBody = Engine.Razor.RunCompile(File.ReadAllText(templateFilePath),
                templateFilePath, model.GetType(), model);
                SendEmailAsync(subj, emailHtmlBody, toEmail, cc, bcc);
            });
        }
        else
        {
            throw new System.Exception("Could not find mail template.");
        }
    }

SendMailAsync 的签名是:

static async Task SendEmailAsync(string subj, string message, string toEmail, string cc = null, string bcc = null)
    {
        //Reading sender Email credential from web.config file  
        string fromEmail = ConfigurationManager.AppSettings["FromEmail"].ToString();
        string fromName = ConfigurationManager.AppSettings["FromName"].ToString();

        //creating the object of MailMessage  
        MailMessage mailMessage = new MailMessage();
        mailMessage.From = new MailAddress(fromEmail, fromName); //From Email Id  
        mailMessage.Subject = subj; //Subject of Email  
        mailMessage.Body = message; //body or message of Email  
        mailMessage.IsBodyHtml = true;

        string[] toMuliId = toEmail.Split(',');
        foreach (string toEMailId in toMuliId)
        {
            mailMessage.To.Add(new MailAddress(toEMailId)); //adding multiple TO Email Id  
        }


        if (cc != null)
        {
            string[] ccId = cc.Split(',');

            foreach (string ccEmail in ccId)
            {
                mailMessage.CC.Add(new MailAddress(ccEmail)); //Adding Multiple CC email Id  
            }
        }

        if (bcc != null)
        {
            string[] bccid = bcc.Split(',');

            foreach (string bccEmailId in bccid)
            {
                mailMessage.Bcc.Add(new MailAddress(bccEmailId)); //Adding Multiple BCC email Id  
            }
        }

        SmtpClient smtp = new SmtpClient
        {
            EnableSsl = true,
            Credentials = new NetworkCredential("", "")
        };

        //network and security related credentials  
        await smtp.SendMailAsync(mailMessage); //sending Email  
    }

没有抛出异常,但我得到了错误:

System.InvalidOperationException:此时无法启动异步操作。异步操作只能在异步处理程序或模块内或在页面生命周期中的某些事件期间启动。如果在执行 Page 时发生此异常,请确保将 Page 标记为 。此异常还可能表示尝试调用“async void”方法,在 ASP.NET 请求处理中通常不支持该方法。相反,异步方法应该返回一个 Task,调用者应该等待它。

【问题讨论】:

  • 基本问题似乎是您的static void SendEmailAsync&lt;TModel&gt;(...) 方法应该是static async Task SendEmailAsync&lt;TModel&gt;(...)。有了它,你可以awaitTask.Run()。这与之前提出的许多问题中讨论的问题完全相同,包括标记的重复项。
  • @PeterDuniho 我只是不想继续在我的所有方法中添加异步关键字,直到控制器。
  • @Aron 是的,谢谢,我认为这是同一个问题。
  • “我只是不想继续将 async 关键字添加到直到控制器的所有方法中” - 如果不这样做,您将无法正确使用 await。那么你接受的答案是如何解决你的问题的呢?
  • @PeterDuniho 我只需要这一种方法发送邮件就可以在后台操作我只需要这一种方法。我只是不想把异步放在任何地方。所以使用 Task.run 我不必把异步放在我调用我的方法的任何地方。

标签: c# asp.net-mvc asynchronous async-await sendmail


【解决方案1】:

这个问题是每次发送电子邮件时您都在运行以下方法(这会生成需要时间的初始类)

Engine.Razor.RunCompile

理想情况下,您应该调用以下方法,然后,如果引发错误,则调用 RunCompile

Engine.Razor.Run

请参阅this article,了解如何使用带有缓存的模板管理器

【讨论】:

  • 理想情况下,您在编译时预编译 Razor...>_
  • 我总是根据需要延迟加载模板,而且我对模板目录运行文件观察程序,它会根据需要使模板缓存无效,因此对模板的更新会实时反映,而无需担心重新编译项目:)
【解决方案2】:

使用这个:

await Task.Run(async () =>
{
    await DoAsyncMethodAsync();
});

【讨论】:

  • 或者你也可以只返回DoAsyncMethodAsync()
  • @Aron,你的意思是await Task.Run(DoAsyncMethodAsync())
  • @MikeHenry 不,我没有。我想你的意思是await Task.Run(DoMethodAsync)
  • @Aron,好的,谢谢。我打算写await Task.Run(() =&gt; DoAsyncMethodAsync())。我没有考虑像您建议的那样调用 Task.Run:await Task.Run(DoMethodAsync)
  • @TomDane await 不会阻塞当前线程 - 这是正确的。然而,main 函数只有在内部异步方法被执行后才会完成它的执行。打开一个新线程将允许主函数在异步调用完成之前完成(就像一个即发即弃的模式)。
猜你喜欢
  • 1970-01-01
  • 2023-04-06
  • 1970-01-01
  • 2016-07-20
  • 1970-01-01
  • 2017-03-12
  • 1970-01-01
  • 2019-03-28
相关资源
最近更新 更多