如何在后台继续执行任务,等待和异步

时间:2015-09-30 19:48:06

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

我正面临着需要帮助以了解如何正确行事的情况。

我有一个行动,是一种预算形式。用户填写所有字段后,用户将按“发送”按钮保存预算。

“发送”按钮将保存数据,然后将向所有公司发送电子邮件。当我说所有公司时,我的意思是大约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吗?

我应该使用Thread吗?

注意事项: 遗憾的是,异步控制器操作在这种情况下没有帮助,因为它们在等待异步操作完成时不会对用户产生响应。它们只解决与线程池和应用程序容量相关的内部问题。

4 个答案:

答案 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)
    }
});