【问题标题】:MVC App - Is this the correct approach for using Async and Await?MVC App - 这是使用 Async 和 Await 的正确方法吗?
【发布时间】:2015-05-12 06:35:03
【问题描述】:

我有一个 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 块是否会被调用。

【问题讨论】:

  • 这个问题是否足够相似以提供帮助:stackoverflow.com/questions/27798039/…
  • @neontapir - 也许但我不确定。我对 async / await 的了解还不够,不知道其中有多少适用。我怀疑这里有一位或多位专家可以快速查看我的情况并确定地告诉我。
  • 我可以看看它,并告诉你这里有一些问题:P 用引用的完整答案解释它是快速部分变得更加复杂的地方。
  • @JohnathonSullinger 这段代码不完整,代码中有很多... 部分,这个问题在codereview 上不会很好接受。
  • @JohnathonSullinger 然后也许将用户指向您推荐的站点的help center

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


【解决方案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
    }

【讨论】:

  • 谢谢!除此之外,其他一切看起来都合适吗?
  • 是的@RandyMinder 其他一切看起来都很好,你一直使用异步,还使用ConfigureAwait 来避免非等待调用的死锁。
【解决方案2】:

这不是用于异步等待的典型方法。我不会觉得说“不正确”很舒服,因为设计目标是如此依赖于实现。

显示的代码中显示了几个问题。由于某些原因,拥有一个采用异步操作并返回 void 的方法可能不是一个好主意。

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

应该使用异步/等待来释放处理能力。在大多数情况下,它只应在处理时间或等待时间超过 40 毫秒时使用。由于电子邮件一发不可收拾,您的情况可能并非如此,在这种情况下,整个方法可以转换回同步。

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

使用 await 会创建一个新的任务延续。该方法的其余部分本质上是在调用发生时分叉的。一旦被调用的执行完成,那么被分叉的方法的其余部分就会执行。如果没有 await,该方法的其余部分只会执行,并且边调用的执行可能会在很晚的时候完成。

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

【讨论】:

  • 我不同意你所说的几件事,我也想知道你为什么提到它们,因为它们与所提出的问题无关。让控制器方法返回 void 是完全可以接受的。而且,任何人都可以调用该方法并发送电子邮件也是错误的。因为你没有看到整个方法,你看不到这是不可能的。但这也与问题无关。
  • @RandyMinder - 您询问了一种方法。异步冒泡了,因此控制器设计中使用的方法是相关的。让控制器方法返回 void 是不好的做法。控制器中的公共方法是可公开访问的。如果你有一个授权属性,那么登录的用户仍然可以访问它。基本上,只要这个动作是由用户引起的,他们也可以通过使用 url 来调用该方法。您有额外的业务逻辑未在您的问题中显示但仍然存在,这只是显示了需要在此处重构的耦合级别。
猜你喜欢
  • 2021-07-30
  • 2021-05-16
  • 2016-01-09
  • 1970-01-01
  • 2015-03-04
  • 1970-01-01
  • 2019-04-21
  • 1970-01-01
  • 2021-07-06
相关资源
最近更新 更多