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

C# MVC应用程序-这是使用Async和Await的正确方法吗?,c#,asp.net-mvc,async-await,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(mast

我有一个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
        }
然后SendSalesEmailsAsynch()如下所示:

    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);
    }
    // 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);
SendSalesEmailAsynch()如下所示:

    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);
    }
    // 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);

我对Async和Wait非常陌生。这一切看起来合适吗?此外,我不完全确定如果发送电子邮件时发生错误,是否会调用控制器操作方法中的catch块

正如您所怀疑的,
catch
块不会得到任何异常,因为您没有与当前上下文同步。为此,您必须将操作方法标记为
async
,并使用
wait
调用
SendSalesEmailsAsync
。因此,动作方法如下所示:

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
    }

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

显示的代码中显示了几个问题。出于以下几个原因,使用一个执行异步操作并返回void的方法可能是个坏主意

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

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

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

使用wait会导致创建新的任务继续。方法的其余部分在调用发生时基本上是分叉的。一旦调用的执行完成,则执行分叉的方法的其余部分。在不等待的情况下,该方法的其余部分将简单地执行,而侧调用的执行可能会在很久以后完成


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

这个问题是否足够相似,可以提供帮助:@neontapir-可能,但我不确定。我对async/await的了解还不够,不知道其中有多少适用。我怀疑这里有一位或多位专家可以快速了解我的情况并肯定地告诉我。我可以看一下,并告诉你这里有一些问题:P用引文解释完整的答案是快速部分变得更复杂的地方。@JohnathonSullinger此代码不完整,代码中有大量的
部分,这个问题在codereview上不会很受欢迎。@JohnathonSullinger然后可能会将用户指向您推荐给他们的站点。谢谢!除此之外,其他一切看起来都很合适?是的@RandyMinder其他一切看起来都很好,您一直在使用async,还使用了
ConfigureAwait
来避免非等待调用的死锁。我不同意您所说的几点,我也想知道为什么您甚至提到了它们,因为它们与所问的问题并不相关。使用控制器方法返回void是完全可以接受的。而且,任何人都可以调用该方法并发送电子邮件也是错误的。由于您没有看到整个方法,因此无法看到这是不可能的。但这也与问题无关。@RandyMinder-你问的是一种方法。异步冒泡出来,所以在控制器设计中使用的方法是相关的。让控制器方法返回void是一种糟糕的做法。控制器中的公共方法是可公开访问的。如果您具有“授权”属性,则登录的用户仍可访问该属性。基本上,每当此操作由用户引起时,他们也可以使用url调用该方法。您的问题中没有显示额外的业务逻辑,但仍然存在,这只是显示了需要在此重构的耦合级别。