最佳实践 - 警告: 方法缺少'await'运算符警告

3
是的,我知道还有其他问题涉及到这个警告的含义以及如何解决它,但是我有一个关于异步编程最佳实践的问题。
我有一个服务层,它处理数据层和表现层之间的数据传输。该服务包含几个方法,用于查询数据库并返回结果。
我一直在尝试在可能的情况下使用异步编程。一个例子:
    public async Task<SiteTemplateResource[]> GetResources(int siteTemplateId, string extension)
    {
        return await Database.FindBy<SiteTemplateResource>(str => str.SiteTemplateId == siteTemplateId && str.HashedFile.EndsWith(extension)).ToArrayAsync();
    }

我的问题是,实际上我并没有等待任何东西,除了 ToArrayAsync 调用,而我其实也不是真的需要它。

我应该继续使用 async/await 吗?如果要在这个函数中等待什么,应该怎么做?

    public async Task<int> SiteTemplateBySiteIdAsync(int siteId)
    {
        return Database.First<SiteSiteTemplate>(sst => sst.SiteId == siteId).SiteTemplateId;
    }

我不需要在那个函数中等待任何东西,但我也不需要调用ToArrayAsync,那么如何避免上述情况中的警告“方法缺少'await'运算符”?谢谢。

2
如果你有 ToArrayAsync,那么你也应该有 FirstAsync。至于第一个方法 - 只需删除 async\await 关键字即可。ToArrayAsync 返回 Task,而你的 GetResources 方法也返回 Task - 这里不需要使用 async\await。 - Evk
@Evk 我不知道那个存在,谢谢。 - nbokmans
如果您返回异步操作的结果,则无需使用async/await。只需使用public Task<...> GetResources(..) => return Database.FindBy.....ToArrayAsync(); - Panagiotis Kanavos
你可以使用 WhereSelectFirstAsync 来使第二个方法也保持异步。 - Panagiotis Kanavos
4个回答

5

如果一个需要使用async的方法中没有需要await的内容(不管出于什么原因),你可以使用Task.FromResult,这个方法可以被await

public async Task<int> SiteTemplateBySiteIdAsync(int siteId)
{
    return await Task.FromResult(Database.First<SiteSiteTemplate>(sst => sst.SiteId == siteId).SiteTemplateId);
}

如果您不需要异步方法,可以简单地将async Task<int>替换为int

4
只要返回一个任务,您甚至可以删除async和await关键字。 只要返回一个任务,就一切都会好。 - Peter Bons
谢谢!这看起来就是我需要的。当你说“需要async的方法”时,你是在暗示并不是所有东西都应该是async的吗?目前我的观点是尽可能让函数成为async,但这是否正确呢? - nbokmans
不一定。有时候你会受到接口的限制,所以即使代码本身不需要,有时候你也需要它们。如果一个方法不需要异步,那么不将其设置为async也是可以的。 - Patrick Hofman
但是如果您在最低层方法中使用了 async,那么您需要一路使用 async - Patrick Hofman

2

异步 API 不一定需要使用 async\await 关键字。首先,您应该问自己,您在方法内调用的任何内容是否使用了 IO 并且具有该 IO API 的异步版本。在您的情况下,您尝试访问数据库,这是 IO 操作,您的库有异步版本(ToArrayAsync),因此一切都很合理。现在检查是否在调用异步 API 后还有其他操作。如果是,请使用 async\await。如果没有,则只需将结果返回给调用者:

public Task<SiteTemplateResource[]> GetResources(int siteTemplateId, string extension)
{
    return Database.FindBy<SiteTemplateResource>(str => str.SiteTemplateId == siteTemplateId && str.HashedFile.EndsWith(extension)).ToArrayAsync();
}

在第二种情况下,您也尝试访问数据库,但是您认为没有异步API可以实现这一点。很可能这不是真的,因为如果您有ToArrayAsync,那么所有的数据库访问方法都有异步版本,所以您应该有FirstAsync。然后您的方法变成了:
public async Task<int> SiteTemplateBySiteIdAsync(int siteId)
{
    var result = await Database.FirstAsync<SiteSiteTemplate>(sst => sst.SiteId == siteId);
    return result.SiteTemplateId;
}

在调用 FirstAsync 后,您需要执行一些操作,因此需要使用 async\await 关键字。


如果您使用WhereSelect,则不需要await。 如果提供程序仅返回SiteTemplateId,则查询可能更有效率。因为现在,它必须返回整个对象。 - Panagiotis Kanavos

1
< p > async/await 仅在您想在方法本身中处理已经异步操作的结果时才需要使用。它们不会使方法或调用异步。如果您不需要处理任务结果,只需返回即可:

public Task<SiteTemplateResource[]> GetResources(int siteTemplateId, string extension)
{
    return Database.FindBy<SiteTemplateResource>(str => 
                           str.SiteTemplateId == siteTemplateId 
                           && str.HashedFile.EndsWith(extension))
                   .ToArrayAsync();
}

或者,使用表达式体方法。
public Task<SiteTemplateResource[]> GetResources(int siteTemplateId, string extension)=>
        Database.FindBy<SiteTemplateResource>(str => 
                        str.SiteTemplateId == siteTemplateId 
                        && str.HashedFile.EndsWith(extension))
                .ToArrayAsync();

如果您使用 WhereSelectFirstAsync,则在第二种情况下也可以避免使用 async/await 并仍然保持异步:

public Task<int> SiteTemplateBySiteIdAsync(int siteId)=>
    Database.Where(sst => sst.SiteId == siteId)
            .Select(it=>it.SiteTemplateId)
            .FirstAsync();
}

这样做的另一个好处是仅从数据库返回ID。如果不使用Select,提供程序将不得不读取整个对象。

0

也许你可以像下面这样改变你的代码:

public int SiteTemplateBySiteIdAsync(int siteId)
{
    return Database.First<SiteSiteTemplate>(sst => sst.SiteId == siteId).SiteTemplateId;
}

祝你有美好的一天!


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接