【问题标题】:Is this the correct usage of async/await in MVC with service/repository layer?这是在带有服务/存储库层的 MVC 中正确使用 async/await 吗?
【发布时间】:2015-03-04 02:00:07
【问题描述】:

使用 VS2013、EF6.1.1、MVC5、.net 4.5。

我刚刚开始第一次研究 async/await,我不确定这是否正确。它似乎工作,但它可以更简单吗?我似乎在很多地方都坚持 async/await 以实现一种方法调用多层。

为简洁起见,所有代码均已简化。

在我的 MVC 控制器操作方法中,我有:

public async Task<ActionResult> TestAction1()
{
    var testResponse = await _testService.TestMethod1(1);

    if (testResponse != null)
    {
        _unitOfWork.CommitAsync();
        return View(testResponse);
    }

    return RedirectToAction("TestAction2");
}

我的服务类如下:

public class TestService : ITestService
{
    public async Task<TestObject> TestMethod1()
    {
        var testObject1 = await privateMethod1Async();
        if (testObject1 != null) return testObject1;

        testObject1 = await privateMethod2Async();

        return testObject1;
    }

    private async Task<TestObject> privateMethod1Async()
    {
        return await _testRepository.FirstOrDefaultAsync();
    }

    private async Task<TestObject> privateMethod2Async()
    {
        return await _testRepository.FirstOrDefaultAsync();
    }
}

还有我的存储库方法:

public async Task<TEntity> FirstOrDefaultAsync()
{
    return await _entitySet.FirstOrDefaultAsync();
}

基本上,我有一个控制器方法,它调用一个服务方法。服务方法是以异步方式调用数据库层两次。但我觉得我正在改变每一个方法和层来处理异步,我不确定我这里的内容是否正确。

其次,在控制器方法中,我不确定如何异步调用工作单元提交方法。具有“_unitOfWork.CommitAsync();”的行。我不能在它前面加上一个“等待”,因为它是一个无效的方法。

有什么想法吗?


编辑 1

这是对 EF 的存储库方法调用的完整版本:

public async Task<TEntity> FirstOrDefaultAsync(Expression<Func<TEntity, bool>> 
                                               predicate, params 
                                               Expression<Func<TEntity, object>>[]
                                               includeProperties)
{
    IQueryable<TEntity> query = EntitySet;
    if (includeProperties != null && includeProperties.Any())
    {
        query = IncludeProperties(query, includeProperties);
    }
    return await query.FirstOrDefaultAsync(predicate);
}

【问题讨论】:

  • 具有“_unitOfWork.CommitAsync();”的行。我不能在它前面加上“等待”,因为它是一个无效的方法。 public async Task CommitAsync() { ... }.
  • 啊,好吧,我想我对它的返回无能为力。所以没有办法使用 await 与我失踪的 void?
  • 我想我对它的回报无能为力。不,你可以await它。
  • 好的,谢谢。剩下的呢?我似乎在为每个方法和层添加相同的 async/await 内容,对吗?

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


【解决方案1】:

我在您的代码中看到一个重复出现的模式:

private async Task<TestObject> privateMethod2Async()
{
    return await _testRepository.FirstOrDefaultAsync();
}

当您有一个单行程序来简单地查询您的数据库以获取 Task&lt;T&gt; 时,您可以避免 await 导致的状态机分配,并且只需将热任务返回给调用者(因为他可能会无论如何都在调用链上等待它):

private Task<TestObject> privateMethod2Async()
{
    return _testRepository.FirstOrDefaultAsync();
}

注意async-await 会让你“一直异步”,这就是异步的本质。例如,如果可能,请确保将可以使用 Task.WhenAll 并发运行的任务分组(不确定这可能是最好的例子):

public async Task<TestObject> TestMethod1()
{
    var testObject1 = await privateMethod1Async();
    if (testObject1 != null) return testObject1;

    testObject1 = await privateMethod2Async();

    return testObject1;
}

也许能变成:

return Task.WhenAny(privateMethod1Async(), privateMethod2Async());

假设其中一种方法可用作返回类型。

编辑:

添加 async/await 的一般经验法则是什么?是 它是一种在其中进行其他处理的方法吗?哪个,正如你所指出的 出来了,这个方法不行吗?

当您想要对返回的 Task 进行更多处理时,您可能想要使用 await。如果只需要返回实际的Task,则无需等待,只需返回热任务即可。这是我使用的一般规则。另请注意,当您使用 return await 与仅使用 return 时,异常处理是不同的。

您可以在At the end of an async method, should I return or await?Any difference between "await Task.Run(); return;" and "return Task.Run()"? 中阅读更多信息

【讨论】:

  • 在您的上一个示例中是否需要async
  • 我的代码已被简化以使其更易于阅读。但是,您确实做到了。添加同步/等待的一般经验法则是什么?是用于在其中进行其他处理的方法吗?正如您所指出的,这种方法没有?
  • @YuvalItzchakov 我已经更新了我的问题,在底部进行了编辑以显示该方法的完整版本。你还会说它不需要等待异步吗?此外,这两个方法调用需要一个接一个地完成。只有第一个为空时才会运行。
  • @eyeballpaul 关于第二部分,这就是为什么我说它可能不是最好的例子,但我只是想传达可以做什么的要点。关于您的编辑,是的,您绝对可以return query.FirstOrDefaultAsync(predicate); 并删除async 修饰符。
  • 感谢您的帮助。我已将较低级别简化为仅返回“任务”,并将 async-await 留给执行更多实际工作的服务级别。
【解决方案2】:

async-await 确实有在您的代码库中向上爬的趋势,这完全没问题。您应该尽可能让它上升(在 UI 事件处理程序中,由于 async-void 和 UI SynchronizationContext 的组合,它可以一直上升到顶部)

当它不再可能时(例如控制台应用程序的根目录),您可以简单地Wait 返回的任务:

var task = RunAsync();
task.Wait();

或者更好,你可以使用Stephen Cleary's AsyncContext:

AsyncContext.Run(RunAsync);

【讨论】:

  • 一直到 DB 层,这是正确的方法吗?考虑到这将是阻碍?
  • @eyeballpaul 是的。否则它就不会真正是异步的。
  • 好的,谢谢。我已经接受了另一个答案,因为它指出了一些事情,一件事是我不需要在较低级别的 async-await ,只返回一个任务。
  • @eyeballpaul 说得对……但无关紧要。
猜你喜欢
  • 1970-01-01
  • 2021-07-30
  • 2018-05-18
  • 2016-01-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-08-14
  • 1970-01-01
相关资源
最近更新 更多