这是 MVC 中具有服务/存储库层的异步/等待的正确用法吗?



Using VS2013, EF6.1.1, MVC5, .net 4.5.

刚刚开始第一次研究异步/等待,我不确定这是否正确。它似乎有效,但它可以更简单吗?我似乎在很多地方都坚持异步/等待,以便一种方法调用多个层。

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

在我的 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);
}

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

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

当你有一个单行只是查询你的数据库以获得Task<T>时,你可以避免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 时,异常处理是不同的。

您可以在异步方法结束时阅读更多内容,我应该返回还是等待? 和 "await Task.Run(); return;" 和 "return Task.Run()"之间有什么区别吗?

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

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

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

或者更好的是,你可以使用Stephen Cleary的AsyncContext

AsyncContext.Run(RunAsync);

最新更新