Linq take 从 OData 集合中返回零个元素

问题描述

我无法解决这个问题,我不明白为什么会这样。 我做了一个 OData 查询,返回 175 个项目的集合

from c in navClient.Item where c.No != "" && c.Last_Date_Modified > dtfrom select c

navClient.Item 是 System.Data.Services.Client.DataServiceQuery

但是我想使用 .Take(100) 从集合中取出前 100 个项目,但得到 0 个项目。直到我这样做 .Take(121) 我才得到我的第一个项目,它是集合中的第一个项目,.Take(122) 返回前两个项目,依此类推。

知道为什么会这样吗?

编辑: 首先执行 ToList 然后 Take(100) 按预期返回前 100。 我唯一知道的理论是,我运行查询的表只是一个数据库不同步的临时表。

解决方法

您已经描述了处理您的请求的服务器实现中的问题。如果在过滤条件之前评估了 .Take(100),则会发生此行为。这个问题很容易发生在客户端服务器实现中。 .ToList() 之所以有效,是因为它将整个集合返回给客户端,然后将过滤条件和标准的linq 应用到对象评估。

虽然不是今天的原因...
作为一般规则,每当您指定 .Take().Skip() 时,您还应该指定显式 .OrderBy(),当限制或分页查询中的顺序不明确时,结果也可能如此。

这里有 3 个常见的级别:

  1. 客户端查询解析
    您的 linq 查询首先被解析为将用于向 API 发出请求的 URL,您应该首先检查该 URL 的构造是否正确。

    您应该期待一个类似于以下内容的网址:

    /odata/Item?$top=100&$filter=No ne '' AND Last_Date_Modified gt '2020-01-20T17:24:21.3605918+11:00'
    

    您可以使用查询中的 .RequestUri 属性检查 URL,尝试使用以下方法来捕获它:

    var query = from c in navClient.Item 
                where c.No != "" && c.Last_Date_Modified > dtfrom 
                select c;
    query = query.Take(100);       
    System.Diagnostics.Debug.WriteLine(((DataServiceQuery<Item>)query).RequestUri);
    

    URL 需要通过 $Top$filter 查询选项到达服务器。

  2. API 控制器
    客户端希望控制器处理或传递 $filter 条件 $top 表达式到底层数据存储。

    许多基于默认 EF 或工作单元的 OData 实现将使用延迟执行简单地返回正确的 IQueryable<T> 表达式。但是,如果后端存储使用存储库模式或控制器以其他方式构建数据集,则该代码可能需要在应用 $filter

    之前显式处理 $top 条件 >

    有许多简单的 OData 控制器示例,它们具有模拟的 List<T> 或其他 IEnumerable<T> 后端,危险信号是,如果您在控制器代码中看到主表达式上的 .ToList().AsQueryable(),或者它返回 IEnumerable<T> 响应,那么它表明该表达式根本使用延迟执行。这意味着它很可能需要手动管理查询选项。

  3. OData 查询表达式解析器
    特别是在 .Net 实现中,默认情况下,EnableQueryAttribute 将查询选项应用于紧接在序列化过程之前或有效地作为序列化过程的一部分的最终 IQueryable 输出。

    因此即使控制器方法已经评估了这些选项,$top$filter(以及 $orderby,$select,$expand)也会再次应用.这通常不是问题,它是验证失败安全的,这意味着您完全不必从控制器方法中返回 IQueryable<T>

    因此,如果我们的后端支持延迟的 IQueryable<T> linq 表达式(如 EF DbSet<T> 中的 DbContext),那么在控制器实现中我们不支持通常对查询选项执行任何操作,您让 EnableQueryAttribute 为您处理。

    但是,如果控制器首先处理分页表达式 .Take(100) 并且没有正确应用 $filter 或根本没有应用,那么 EnableQueryAttribute 会将条件应用于具有已经受到限制,在您的示例中,可能是前 100 项