Reputation: 535
I use this two actions to send email to admins about inventory status when it's time to make an order... But When I call SendEmailToAdminUser .. it takes time and side by side I cant work with other functions... waiting litle long.. I want to change those functions to async and await Task ... but I don't how to do. I tried to change from JsonResult to 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)
}
And my SendEmail function is:
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;
}
}
Upvotes: 2
Views: 239
Reputation: 4783
As mentioned by commenters you won’t get any benefits from async/await as you’ll just be releasing the underlying thread for other requests. Actual execution time will likely be longer than what you currently have.
It sounds like what you actually want is concurrency which can be easily achieved using a Parallel.ForEach
. However, before complicating your code I’d suggest a couple of changes first.
SmtpClient
. There is overhead in instantiating this for every call because it’ll need to connect to the backend SMTP service every time.SendMail
.From your code above:
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;
}
}
That's fairly thrown together but should hopefully give you a start. In essence you just need to try to minimise the amount of I/O you're doing and you should see substantial performance improvements!
Upvotes: 1