【问题标题】:Asp.Net mvc, How to make async and await methodAsp.Net mvc,如何制作异步和等待方法
【发布时间】:2018-03-12 17:52:59
【问题描述】:

当需要下订单时,我会使用这两个操作向管理员发送有关库存状态的电子邮件... 但是当我打电话给 SendEmailToAdminUser .. 需要时间和并排我不能使用其他功能...等待很长时间.. 我想将这些功能更改为异步并等待任务......但我不知道该怎么做。我试图从 JsonResult 更改为 ActionResult ...

public JsonResult SendEmailToAdminUser()
{
    bool result = false;

    var emails = UserEmails();

    if (emails != null)
    {
        foreach (var email in emails)
        {
            var departmentId = context.Users.Where(e => e.Email == email)
                .Select(s => s.DepartmentId).FirstOrDefault();

            string departmentname = context.Departments.Where(u => u.deptId == departmentId)
                .Select(n => n.DepartmentName).Single();

            int[] ProductId = context.Inventory.Where(s => s.prId == departmentId)
                .Where(s => s.Status == 10).Select(s => s.PrId).ToArray();

            foreach (var product in ProductId)
            {
                string productname = context.Products.Where(n => n.prId == product)
                    .Select(n => n.ProductName).Single();

                result = SendEmail(email, productname, "<p> Hi there, <br />It is time to order " 
                    + productname + " to department: " + departmentname + "</p> <br /> /Regards");
            }
        }
    }

    return Json(result, JsonRequestBehavior.AllowGet)
}

而我的 SendEmail 功能是:

public bool SendEmail(string toEmail, string subject, string emailBody)
{
    try
    {
        string senderEmail = System.Configuration.ConfigurationManager.AppSettings["SenderEmail"].ToString();
        string senderPassword = System.Configuration.ConfigurationManager.AppSettings["SenderPassword"].ToString();

        SmtpClient client = new SmtpClient("smtp.gmail.com", 587);
        client.EnableSsl = true;
        client.Timeout = 100000;
        client.DeliveryMethod = SmtpDeliveryMethod.Network;
        client.UseDefaultCredentials = false;
        client.Credentials = new NetworkCredential(senderEmail, senderPassword);

        MailMessage mailMessage = new MailMessage(senderEmail, toEmail, subject, emailBody);
        mailMessage.IsBodyHtml = true;
        mailMessage.BodyEncoding = UTF8Encoding.UTF8;
        client.Send(mailMessage);

        return true;
    }
    catch(Exception ex)
    {
        return false;
    }
}

【问题讨论】:

  • 使它async 不会对花费的时间产生任何影响(它只是意味着您释放线程以便它可以被其他请求使用)
  • 而你的return Json(result, ...) 没有多大意义。如果前 20 个失败,但最后一个成功,则您的方法返回 true
  • 您可能会考虑将所有产品名称合并到一封电子邮件并发送给用户。至少这会将它从 O(M*N) 更改为 O(M) 调用
  • @StephenMuecke 所以你的意思是它还不错..?我有两个按钮.. + 和 - 增加和减少产品...当我单击减少按钮并且当产品状态减少到 10 然后它发送电子邮件...我不得不等待更多减少,我的减少发送电子邮件时按钮冻结。但它只是几秒钟..所以我可以忍受,,;)

标签: asp.net-mvc asynchronous async-await


【解决方案1】:

正如评论者所说,您不会从 async/await 中获得任何好处,因为您只会为其他请求释放底层线程。实际执行时间可能会长比您目前拥有的时间。

听起来您真正想要的是并发,可以使用Parallel.ForEach 轻松实现。但是,在使您的代码复杂化之前,我建议先进行一些更改。

  • 重新使用SmtpClient 的实例。每次调用都需要实例化它,因为它每次都需要连接到后端 SMTP 服务。
  • 正如 Shyju 指出的,优化您的算法。现在,您正在循环发送多封电子邮件到相同电子邮件地址。您可以将它们合并在一起,以尽量减少对SendMail 的调用量。
  • 通过一次调用多个事物来最大限度地减少数据库调用次数。

根据您上面的代码:

public JsonResult SendEmailToAdminUser()
{
    bool result = false;

    var emails = UserEmails();

    if (emails != null)
    {
        // fetching data in batches
        var departmentIdsByEmail = context.Users
            .Where(x => emails.Contains(x.Email))
            .Select(x => new { x.Email, x.DepartmentId })
            .ToDictionary(x => x.Email, x => x.DepartmentId);

        var departmentIds = departmentIdsByEmail.Values;

        var departmentNamesById = context.Departments
            .Where(x => departmentIds.Contains(x.deptId))
            .ToDictionary(x => x.deptId, x => x.DepartmentName);

        var productNamesByDepartmentId = context.Inventory
            .Where(x => departmentIds.Contains(x.deptId))
            .Where(x => x.Status == 10)
            .GroupBy(x => x.deptId)
            .ToDictionary(g => g.Key, g => g.Select(x => x.Product.Name).ToArray());

        foreach (var emailWithDepartmentId in departmentIdsByEmail)
        {
            // send **one** email per address rather than many
            var email = emailWithDepartmentId.Key
            var departmentId = emailWithDepartmentId.Value;
            var departmentName = departmentNamesById[departmentId];
            var productNames = productNamesByDepartmentId[departmentId];

            result = SendEmail(
                email, 
                "<p> Hi there, <br />It is time to order "                      + string.Join(", ", productNames) + " to department: " + departmentName + "</p> <br /> /Regards");

        }
    }

    return Json(result, JsonRequestBehavior.AllowGet)
}

private static readonly SmtpClient _smtpClient = CreateSmtpClient();

private static SmtpClient CreateSmtpClient()
{
     string senderEmail = System.Configuration.ConfigurationManager.AppSettings["SenderEmail"].ToString();
     string senderPassword = System.Configuration.ConfigurationManager.AppSettings["SenderPassword"].ToString();

     SmtpClient client = new SmtpClient("smtp.gmail.com", 587);
     client.EnableSsl = true;
     client.Timeout = 100000;
     client.DeliveryMethod = SmtpDeliveryMethod.Network;
     client.UseDefaultCredentials = false;
     client.Credentials = new NetworkCredential(senderEmail, senderPassword);

     return client;
}

public bool SendEmail(string toEmail, string subject, string emailBody)
{
    string senderEmail = System.Configuration.ConfigurationManager.AppSettings["SenderEmail"].ToString();
    try
    {
        MailMessage mailMessage = new MailMessage(senderEmail, toEmail, subject, emailBody);
        mailMessage.IsBodyHtml = true;
        mailMessage.BodyEncoding = UTF8Encoding.UTF8;
        _smtpClient.Send(mailMessage);

        return true;
    }
    catch(Exception ex)
    {
        return false;
    }
}

这些内容相当统一,但希望能给你一个开始。本质上,您只需要尽量减少您正在执行的 I/O 数量,您应该会看到显着的性能提升!

【讨论】:

  • 感谢您的回复。我的代码有两个错误。第一个是 CreateSmtpClient() 中的返回错误错误消息是“需要可转换为'SmtpClient'的类型的对象”这反过来会发生;另一种是Inside SendEmail方法MailMessage mailMessage = new MailMessage(senderEmail, toEmail, subject, emailBody);错误消息是“当前上下文中不存在名称'senderEmail'”也许你必须定义也许这个名称没有在SendEmail方法中定义......再次感谢
  • 我做了调整,但我没有编译或测试这里写的代码。这更像是一种了解如何修改和优化现有代码的方式!
  • 谢谢,现在没有错误,现在只有一个错误。 SendEmailToAdminUser() 中的 productNamesByDepartmentId 在 ToDirection 中抛出 NullReferenceException ... .ToDictionary(g => g.Key, g => g.Select(x => x.Product.Name).ToArray());错误消息 = 'x.Product.Name' 引发了“System.NullReferenceException”类型的异常
  • 您需要修改它以使用您的数据库上下文。我不知道它是如何配置的。想必你在产品和库存之间有关系,所以修改代码来修复它!
猜你喜欢
  • 2016-08-29
  • 2014-05-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-07-21
  • 1970-01-01
相关资源
最近更新 更多