我有以下控制器方法
public async Task<ActionResult> SendToAllUsers(SentMailToAllUsersModel model)
{
if (ModelState.IsValid)
{
var mail = MailService.SendMailToAllUsers(model.Body, model.Title);
await mail;
}
return View(model);
}
在邮件服务上调用此方法
public Task SendMailToAllUsers(string content, string title)
{
var users = UserService.GetAllUsers();
var mailTemplates = users.Result.AsParallel().Select(user =>
{
var mailTemplate = new MastersMailTemplate(user);
mailTemplate.HtmlEmailTemplate = content;
mailTemplate.Subject = title;
mailTemplate.From = _fromEmail;
return Task.Factory.StartNew(() => MailProvider.SendEmailAsync(mailTemplate.CreateMailMessage(), new ResultDescription()).ConfigureAwait(false));
}).ToArray();
return Task.WhenAll(mailTemplates);
}
此方法触发执行此方法的邮件提供程序:
public Task<IResultDescription> SendEmailAsync(MailMessage message, IResultDescription rd)
{
// Create our SMTP Client
SmtpClient client = new SmtpClient();
client.Host = SmtpServer;
client.Port = SmtpServerPort;
client.Credentials = new NetworkCredential(SmtpServerUsername, SmtpServerPassword);
client.EnableSsl = true;
if (AppSettings.IsInTestMode)
{
Log.Info("Test mode check: Removing all emails and replace to test");
message.To.Clear();
foreach (var email in AppSettings.DefaultTestEmail)
{
message.To.Add(new MailAddress(email));
}
}
client.Timeout = 10;
Log.Info("Sending Email to" + message.To.FirstOrDefault());
var task = Task.Run(async () =>
{
try{
client.SendCompleted += (s, e) =>
{
client.Dispose();
message.Dispose();
};
await client.SendAsync(message);
rd.Success = true;
return rd;
}
catch (Exception e)
{
Log.Error("Email not send");
rd.Success = false;
if (rd.Errors == null)
{
IList<string> errors = new List<string>();
errors.Add(e.Message);
rd.Errors = errors;
}
else
{
rd.Errors.Add(e.Message);
}
return rd;
}
});
return task;
}
问题是在发送的任何邮件之前返回结果视图 控制器没有等待,直到所有邮件都被发送。
如何在邮件服务中的所有任务完成后确保控制器仅继续执行?
答案 0 :(得分:2)
作为一般规则,请勿在ASP.NET上使用Task.Run
,Task.Factory.StartNew
,Parallel
或PLINQ。总有一种更好的方法。在这种情况下,只需使用async
和await
:
public async Task SendMailToAllUsersAsync(string content, string title)
{
var users = await UserService.GetAllUsersAsync();
var mailTemplates = users.AsParallel().Select(user =>
{
var mailTemplate = new MastersMailTemplate(user);
mailTemplate.HtmlEmailTemplate = content;
mailTemplate.Subject = title;
mailTemplate.From = _fromEmail;
return MailProvider.SendEmailAsync(mailTemplate.CreateMailMessage());
}).ToArray();
return await Task.WhenAll(mailTemplates);
}
类似于你的内在方法:
public Task<IResultDescription> SendEmailAsync(MailMessage message, IResultDescription rd)
{
using (SmtpClient client = new SmtpClient())
using (message)
{
client.Host = SmtpServer;
client.Port = SmtpServerPort;
client.Credentials = new NetworkCredential(SmtpServerUsername, SmtpServerPassword);
client.EnableSsl = true;
if (AppSettings.IsInTestMode)
{
Log.Info("Test mode check: Removing all emails and replace to test");
message.To.Clear();
foreach (var email in AppSettings.DefaultTestEmail)
{
message.To.Add(new MailAddress(email));
}
}
client.Timeout = 10;
Log.Info("Sending Email to" + message.To.FirstOrDefault());
try
{
await client.SendAsync(message);
rd.Success = true;
}
catch (Exception e)
{
Log.Error("Email not send");
rd.Success = false;
if (rd.Errors == null)
{
IList<string> errors = new List<string>();
errors.Add(e.Message);
rd.Errors = errors;
}
else
{
rd.Errors.Add(e.Message);
}
}
return rd;
}
}
请记住,async
使容易。如果async
代码过于复杂,请检查更好的方法。我的博客上有async
intro您可能会觉得有用。
答案 1 :(得分:0)
我认为问题出在您的SendMailToAllUsers
方法中。我认为您需要await
MailProvider.SendEmailAsync
来电。如果您不这样做,那么只要该方法执行,Task.Factory.StartNew
启动的任务就会被视为已完成。因为该方法实际上是异步的,所以它只会启动操作,它不会等待它的完成。如果你await
应该解决问题的结果。
将您的代码更改为:
public Task SendMailToAllUsers(string content, string title)
{
var users = UserService.GetAllUsers();
var mailTemplates = users.Result.AsParallel().Select(user =>
{
var mailTemplate = new MastersMailTemplate(user);
mailTemplate.HtmlEmailTemplate = content;
mailTemplate.Subject = title;
mailTemplate.From = _fromEmail;
// Await the result of the lambda expression
return Task.Factory.StartNew(() => await MailProvider.SendEmailAsync(mailTemplate.CreateMailMessage(), new ResultDescription()).ConfigureAwait(false));
}).ToArray();
return Task.WhenAll(mailTemplates);
}