我正面临着需要帮助以了解如何正确行事的情况。
我有一个行动,是一种预算形式。用户填写所有字段后,用户将按“发送”按钮保存预算。
“发送”按钮将保存数据,然后将向所有公司发送电子邮件。当我说所有公司时,我的意思是大约1000家公司。
但问题是,当我按下按钮发送时,页面在发送公司的电子邮件时被锁定,并且在向所有公司发送电子邮件后,网站返回视图。
我想做的是,当用户按下发送按钮时,网站会保存预算,开始在后台发送电子邮件,并且几乎立即将视图返回给用户,但是,这个,网站正在向1000家公司发送背景电子邮件。
我是怎么做到的?这是我的方法的签名:
public async Task<PartialViewResult> EnviarProposta(PostOrcamentoServicoProposta proposta)
{
// persist the budget
SaveData(proposta);
// get all companies...
var companies = getCompanies(proposta);
foreach (var company in companies)
await EmailFactory.SendBudget(proposta, company).SendAsync();
return PartialView(proposta);
}
SendAsync是SMTP的异步方法。
问题是,如何简单地将发送电子邮件方法抛出到后台并返回视图,而不等待发送完成?
第二个问题是我使用的是一个在电子邮件中转换视图的框架,因此,我们需要控制器的上下文。
我应该使用task.factory.startnew吗?
注意事项: 遗憾的是,异步控制器操作在这种情况下没有帮助,因为它们在等待异步操作完成时不会对用户产生响应。它们只解决与线程池和应用程序容量相关的内部问题。
答案 0 :(得分:3)
由于你是通过asp.net托管的,所以正确的方法有点复杂。
通过线程排队工作不会通知运行时有工作要做。这意味着当应用程序域被回收时(在没有您干预的情况下不时完成),您的排队工作无法保证完成。如果这与您无关,请继续排队后台任务。
执行此操作的“正确”方法是以某种方式持续该工作(消息队列,SQL服务器等...)并让其他主机运行它。
有关详细信息/可能的解决方案,请参阅此blog post。
答案 1 :(得分:2)
我喜欢排队需要发送的电子邮件,并让其他服务实际执行发送......
...特别是因为看起来每封电子邮件都有一些工作要做。
例如,您可以在数据库中创建一个条目,其他服务从该条目工作,并将它们标记为已发送。如果您的电子邮件内容实际上是通过调用视图生成的,那么单独的服务仍然可以通过 WebRequest 或类似方式简单地调用相应的操作。
然后你也可以将工作量标记为已完成,并有证据证明这一事实。
答案 2 :(得分:0)
有很多方法可以做到这一点。例如,您只需从上面的代码中删除等待即可。或者您可以使用LINQ并使您的任务数组使用continuation。
Task[] tasks = companies.Select(c => EmailFactory.SendBudget(proposta).SendAsync(c)).ToArray();
Task.Factory.ContinueWhenAll(tasks, t => { /*do something*/ });
(我假设您需要以某种方式将公司传递给每个Send方法调用)
或者正如艾哈迈德所提到的,你可以通过这种方式开始一个主题并发送电子邮件。
答案 3 :(得分:-2)
如果你需要一个&#34; Fire and Forget&#34;方法,考虑使用Task.Factory.StartNew()。
Task.Factory.StartNew( () =>
{
foreach(Company c in companies)
{
// send your email here, make sure you have good error handling!
EmailFactory.SendBudget(proposta)
}
});