C# WebAPI OData v4查询不异步?

C# WebAPI OData v4查询不异步?,c#,entity-framework,asynchronous,asp.net-web-api,odata,C#,Entity Framework,Asynchronous,Asp.net Web Api,Odata,我一直在四处寻找这个问题的答案,但似乎找不到任何明确的答案。 我们使用ODataV4,使用ODataQueryOptions.apply将OData选项应用于查询。我们还使用ODataQuerySettings设置页面大小。当我们设置页面大小时,我们不能再对从ODataQueryOptions.ApplyTo返回的IQueryable使用toListSync()。错误消息表示IQueryable的提供程序不再来自实体框架 我发现这是因为在使用pagesize时,OData通过将IQueryabl

我一直在四处寻找这个问题的答案,但似乎找不到任何明确的答案。 我们使用ODataV4,使用ODataQueryOptions.apply将OData选项应用于查询。我们还使用ODataQuerySettings设置页面大小。当我们设置页面大小时,我们不能再对从ODataQueryOptions.ApplyTo返回的IQueryable使用toListSync()。错误消息表示IQueryable的提供程序不再来自实体框架

我发现这是因为在使用pagesize时,OData通过将IQueryable传递给TruncatedCollection来解析它。此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));
    }
}
public class CustomerController : BaseController
{
    ICustomerService customerService;

    public async Task<IHttpActionResult> Get(ODataQueryOptions<Customer> options)
    {
        var query = customerService.Query();
        return await ODataResult(query, options);
    }
}
由于我们在DefaultSettings中定义了pagesize,因此已执行数据库查询。定义pagesize会导致ApplyTo中的底层代码从数据库中检索所有数据,然后将检索到的列表作为可查询项返回。这意味着在同步函数中查询数据库


所以,我的问题是:有没有一种方法可以在不放弃异步读取的情况下实现对OData的分页?或者我在尝试执行此操作时是否过度复杂了?

我不确定您为什么要尝试调用
ToListAsync()
。应该没有必要。在您的操作方法中,您应该组合查询,而不是实际获取任何数据。所有这些都应该很简单,不需要异步。
IQueryable
稍后由框架执行。(框架还应自动将查询字符串中的所有OData筛选器参数应用于返回的查询,包括分页)

事实上,拥有类型为
IQueryable
(或
IEnumerable
)的异步结果是没有意义的()。不能异步枚举


理论上,您可以异步地将所有结果放入数组中,但是Odata会在内存中应用其过滤器,而不是针对查询。我不确定您为什么需要这样做,但是使用Odata没有什么意义。

可以在Odata中实现分页而不放弃异步读取。分页基本上意味着对IQueryable实例应用一个以上的表达式。在调用IQueryable.Take之后,可以调用IQueryable.ToListSync(实际发生枚举的地方)

但是Microsoft Web Api OData v4实现使得查询枚举是同步进行的。看见ODataQuerySettings.ApplyTo方法正在内部使用TruncatedCollection。TruncatedCollection继承自System.Collections.Generic.List,创建时将IQueryable构造函数参数传递给List,List接受IEnumerable,对其进行迭代并复制到内部数组中


因此,您可以分叉WebAPI OData(因为它是开源的),调整它并使其异步。或者实现您自己的ODataQuerySettings.ApplyTo版本,该版本将是异步的。

您能提供一些代码吗?根据您的解释,我认为在查询末尾添加
.AsQueryable()
可能会解决问题。不确定您是在要求解决
IQueryable.toListSync
问题还是要求异步
ODataQueryOptions.ApplyTo
。指定了我的问题并提供了一些代码。遗憾的是,AsQueryable()不起作用。当ApplyTo返回时,数据已经从数据库中检索到了。我也被这个问题难住了。我认为症结在于,任何地方都没有可以利用的IQueryableAsync接口。.toListSync()是EF中的一个IQueryable扩展,我确信OData的人不想与EF结合。如果只有像IQueryableAsync或其他东西这样的公共接口可以共享,那么提供者就可以允许异步执行,使用者就可以调用它。事实上,我们的get似乎必须是同步的,这很不幸,因为我们通常得到的比Save还多。@AndriiLitvinov它在使用分页时肯定会列举它。当我亲自查看源代码时,我就知道了这一点。我没能解决这个问题。解决这个问题的唯一方法是编写自己的ApplyTo实现,我不愿意花时间在它上面。我们决定使用更简单的路由,只需在控制器上使用EnableQuery属性,并让OData完成它的工作。结果产生的SQL查询远非理想,但也不尽然。性能和可伸缩性还不是我们所关心的问题。重点不是我想使用
ToListAsync()
,而是异步执行数据库查询的一种方法。我不想阻塞数据库查询。没有分页,
ToListAsync()
就可以了。就我所知,这样称呼没有什么坏处。当然,返回一个
IQueryable
同样有效,因为OData处理查询表的解析。但是,我只想强制读取是异步的。有一点您可能从我的问题中误解了:“但是Odata将在内存中应用其过滤器,而不是针对查询”
ApplyTo
将在执行查询之前应用其过滤器,因此这不是真的。我说过“理论上可以将所有结果异步地放入数组中,但是Odata将在内存中应用其过滤器”。绝对会的
    IQueryable result = options.ApplyTo(query, DefaultSettings);