MVC应用程序 - 这是使用Async和Await的正确方法吗?

时间:2015-05-11 22:14:42

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

我有一个MVC控制器方法可以做很多事情,但它最后做的是:

    public void PerformCheckout(int salesAddressId)
    {
        try
        {
            ...
            ...
            // We just made a sale. Send emails to all market owners.
            SendSalesEmailsAsync(master);
        }
        catch (System.Exception exception)
        {
            // Log the error
        }

然后SendSalesEmailesAsynch()看起来像这样:

    private async Task SendSalesEmailsAsync(SalesMaster salesMaster)
    {
        ...
        ...
        // Send an email to the owner of each marker
        foreach(string market in markets)
            await SendSalesEmailAsync(salesMaster, salesDetailList, market).ConfigureAwait(false);
    }

SendSalesEmailAsynch()如下所示:

    // Send an email to a market owner
    private async Task SendSalesEmailAsync(SalesMaster salesMaster, List<SalesDetail> salesDetail, string marketName)
    {
        ...
        ...
        Email email = new Email(new List<string> { sellerEmailAddress }, emailSubject, emailHtmlBody);
        await email.SendAsync().ConfigureAwait(false);

最后,实际发送电子邮件的方法是:

    public async Task SendAsync()
    {
        // Create a network credentials object
        var credentials = new NetworkCredential(azureUserName, azurePassword);

        // Create an Web transport for sending the email
        var transportWeb = new Web(credentials);

        // Send the email. We don't care about the current context so let's run this in a thread pool context.
        // For more information: http://blog.stephencleary.com/2012/07/dont-block-on-async-code.html
        await transportWeb.DeliverAsync(this._email).ConfigureAwait(false);
    }

我是Async和Await的新手。这一切看起来都合适吗?此外,我不完全确定如果在发送电子邮件时发生错误,将调用控制器操作方法中的catch块。

2 个答案:

答案 0 :(得分:1)

如您所料,由于您未与当前上下文同步,catch块无法获得任何异常。为此,您必须将操作方法​​标记为async,并使用await来调用SendSalesEmailsAsync。因此action方法如下所示:

public async Task PerformCheckout(int salesAddressId)
{
    try
    {
        ...
        ...
        // We just made a sale. Send emails to all market owners.
        await SendSalesEmailsAsync(master);
    }
    catch (System.Exception exception)
    {
        // Log the error
    }

答案 1 :(得分:1)

这不是用于异步等待的典型方法。我不愿意说“不正确”,因为设计目标是依赖于实现的。

显示的代码中有几个问题。有一个采取异步操作并返回void的方法可能是一个坏主意,原因有几个。

控制器不应该真正暴露不包含返回信息的功能。如果它是要执行的操作,则应将其称为某种服务或业务逻辑。此外,由于该方法是公共的,并且控制器的一部分任何人都可以从URL调用它。由于它发送了一组电子邮件,这可能是不可取的。

应该使用Async / await来释放处理能力。在大多数情况下,只应在处理时间或等待时间超过40毫秒时使用。由于电子邮件是火上浇油而忘记,在您的情况下可能不是这种情况,在这种情况下,整个方法可以转换回同步。

如果花费的时间超过40毫秒,那么异步就可以使用了。但是,在显示的代码中,第一次调用未正确使用。如果在没有等待的情况下进行异步调用,它基本上就是“发射并忘记”。特别是当没有返回值时,从未注意到返回。因此,如果抛出异常,它也会被遗忘,并且catch块将永远不会被执行。

使用await会导致创建新的任务延续。在调用发生时,该方法的其余部分基本上是分叉的。一旦被调用的执行完成,那么分叉的方法的其余部分就会执行。如果没有等待,方法的其余部分只会执行,而旁边调用的执行可能会在很晚之后完成。

总而言之,这个设计应该重构,以便从更受控制的环境中调用,甚至可能从同步方法调用。