我一直在四处寻找这个问题的答案,但我似乎无法找到任何明确的答案。 我们使用OData v4,使用ODataQueryOptions.ApplyTo将OData选项应用于查询。我们还使用ODataQuerySettings来设置 pagesize 。当我们设置页面大小时,我们不能再使用从ODataQueryOptions.ApplyTo返回的IQueryable上的ToListAsync()。错误消息表明IQueryable的提供程序不再来自Entity Framework。
我发现这是因为当使用 pagesize 时,OData通过传递TruncatedCollection来解析IQueryable。此TruncatedCollection从数据库中检索所有(pagesize + 1)结果,以检查是否有超过 pagesize 结果。但是,ApplyTo不是异步方法,因此我可以安全地假设此数据库查询不是异步执行的。
我能做些什么来确保查询是异步执行的吗?当然OData团队已经想到了这个?或者它是否可以保持同步?对我而言,现在似乎异步IO几乎是必需的,因为我们希望我们的API能够很好地扩展,并且在等待IO时不会阻止所有线程。
感谢您的帮助!
修改1:
我被要求提供一些代码来解释我的意思。
在BaseController.cs中:
public class BaseController : ODataController
{
private static readonly ODataQuerySettings DefaultSettings = new ODataQuerySettings() { PageSize = 60 };
protected Task<IHttpActionResult> ODataResult<T>(IQueryable<T> query, ODataQueryOptions<T> options)
{
IQueryable result = options.ApplyTo(query, DefaultSettings);
return Task.FromResult(ODataOk(result));
}
}
在CustomerController.cs中:
public class CustomerController : BaseController
{
ICustomerService customerService;
public async Task<IHttpActionResult> Get(ODataQueryOptions<Customer> options)
{
var query = customerService.Query();
return await ODataResult(query, options);
}
}
正如我上面所说,问题出在ApplyTo的底层代码中。这是OData本身的一种方法。 这一行:
IQueryable result = options.ApplyTo(query, DefaultSettings);
已经执行了数据库查询,因为我们在DefaultSettings中定义了pagesize。定义pagesize会导致ApplyTo中的底层代码从数据库中检索所有数据,然后将检索到的列表作为可查询返回。这意味着在同步函数中查询数据库。
所以,我的问题是:有没有办法在不放弃异步读取的情况下实现对OData的分页?或者在尝试这样做时我是否过于复杂?
答案 0 :(得分:0)
可以在OData中实现分页而不放弃异步读取。分页基本上意味着再向IQueryable实例应用一个表达式。在调用IQueryable.Take之后,可以调用IQueryable.ToListAsync(实际上会发生枚举)。
但是Microsoft Web Api OData v4实现是这样的,即查询枚举是同步发生的。见here。 ODataQuerySettings.ApplyTo方法在内部使用TruncatedCollection。 TruncatedCollection继承自System.Collections.Generic.List,在创建时,它将IQueryable构造函数参数传递给List接受IEnumerable的constructor,迭代它并复制到内部数组。
所以,你可以分叉Web Api OData(因为它的开源),调整它并使其异步。或者实现你自己的ODataQuerySettings.ApplyTo版本,它将是异步的。
答案 1 :(得分:-3)
我不确定你为什么打电话给ToListAsync()
。应该没有必要。在您的操作方法中,您应该编写查询,但不实际获取任何数据。所有这一切都应该是微不足道的,不需要异步。 IQueryable
稍后由框架执行。 (框架还应自动将查询字符串中的所有OData过滤器参数应用于您已返回的查询,包括分页。)
事实上,拥有类型为IQueryable
(或IEnumerable
的异步结果)是没有意义的(see this answer)。您不能异步枚举。
理论上,您可以异步地将所有结果都放入数组中,但Odata会将其过滤器应用于内存而不是查询。我不确定你为什么要这么做,但是使用Odata的意义非常小。