【问题标题】:Adding custom paging to OData(v4) Web API C#将自定义分页添加到 OData(v4) Web API C#
【发布时间】:2016-11-02 22:48:00
【问题描述】:

我想知道是否有人可以帮助我。我正在尝试在 OData 提要(oData v4)中实现自定义分页。我正在从一个存储过程中填充 IEnumerable,我在下面添加了一种分页方式(工作完美)

SELECT  *
FROM  ( 
    SELECT    ROW_NUMBER() OVER ( ORDER BY OrderID ) AS RowNum, *
    FROM      dbo.Order
    WHERE     CompanyID = @CompanyID
) AS RowConstrainedResult
WHERE   RowNum >= @Start AND RowNum <= @Finish
ORDER BY RowNum

但是我遇到的问题是,除了分页不起作用之外,以下代码可以完美运行

[EnableQuery]
public async Task<PageResult<Order>> GetOrders(ODataQueryOptions<Order> queryOptions)
{
    int CompanyID = User.Identity.GetCompanyID().TryParseInt(0);

    ODataQuerySettings settings = new ODataQuerySettings()
    {
        PageSize = 100,
    };

    int OrderCount = _OrderRepo.GetOrderCount(CompanyID);
    int Skip = 0;
    if (queryOptions.Skip != null)
    {
        Skip =  queryOptions.Skip.Value;
    }

    IEnumerable<Order> results = await _OrderRepo.GetAll(CompanyID, Skip, 100);

    IQueryable result = queryOptions.ApplyTo(results.AsQueryable(), settings);

    Uri uri = Request.ODataProperties().NextLink;
    Request.ODataProperties().TotalCount =  OrderCount;

    PageResult<Order> response = new PageResult<Order>(
    result as IEnumerable<Order>,
    uri, Request.ODataProperties().TotalCount);

    return response;
}

基本上,我试图以 100 个为一组对结果进行分页,仅在需要时从数据库中获取 100 个。

当我第一次调用该控制器时,我得到了预期的结果。

http://localhost:24600/Data/Orders

但是当我查询这个 nextLink(如下)时,尽管数据库中有超过 50,000 个结果并且 IEnumerable 结果已正确更新,但我没有收到任何错误但没有结果。

http://localhost:24600/Data/Orders?$skip=100

如果有人帮助我解决这个问题,我将非常感激,因为我是使用 odata 的新手。

更新

看起来,它正在计算第一批的大小并将其作为总大小,即使我正在设置总大小。它似乎无法超过初始大小。

【问题讨论】:

  • 取自文档:对于大型实体集,客户可能希望限制结果的数量。例如,客户可能一次显示 10 个条目,并带有“下一个”链接以获取下一页结果。为此,客户端使用 $top 和 $skip 选项。 localhost/Products?$top=10&$skip=20
  • 当我尝试实际查询数据时(例如 ?$top=10&$skip=20),即使我使用了 top),幕后的事情正在发生并且没有显示 IEnumerable 结果,尽管事实上他们在那里。例如,我将下一批放入响应中,当我通过在返回上放置一个断点进行调试时,我可以看到结果,但它拒绝显示它们。奇怪的是没有错误。

标签: c# .net asp.net-web-api asp.net-web-api2 odata


【解决方案1】:

我认为 EnableQuery 属性是问题所在。似乎这告诉框架您希望它处理事情。我下面的示例,sans 属性,在 asp.net 核心上使用 odata(我认为唯一真正的区别是使用 ODataFeature 而不是 ODataProperties)...

public class ThingController : ODataController
{
    [HttpGet]
    public PageResult<Thing> GetThings(ODataQueryOptions<Thing> queryOptions)
    {
        var thingsToSkip = queryOptions?.Skip?.Value ?? 0;
        var pageSize = 10;

        var totalThings = GetThingCount();

        var pageOfThings = GetThings(thingsToSkip, pageSize);

        var showNextLink = thingsToSkip + pageOfItems.Count < totalThings;

        return new PageResult<Things>(pageOfThings,
            showNextLink ? Request.GetNextPageLink(pageSize) : null,
            totalThings);
    }

    private int GetThingCount()
    {
        return 21;
    }

    private List<Things> GetThings(int skip, int take)
    {
        return Enumerable.Range(0, TotalThingCount())
            .Select(r => new Thing { Id = r.ToString() })
            .Skip(skip)
            .Take(take)
            .ToList();
    }

【讨论】:

    【解决方案2】:

    您可以只返回完整集(如IQueryable)并让 OData 处理分页:

    [EnableQuery(PageSize = 100)]
    public IQueryable<Order> Get()
    {
        return _OrderRepo.GetAll(CompanyID); //Assuming this returns an IQueryable
    }
    

    OData 知道如何处理 IQueryable 并将使用 .Top().Skip() 获取条目,这将由您的可查询提供程序转换为 SQL。 (大概是 EF?)

    【讨论】:

    • 问题是数据集非常大,一次获取所有数据。它的记录超过 50k。这意味着如果我使用开箱即用的东西,它真的很慢。
    • 如果您使用实体框架(这是一个选项吗?),您可以使用 IQueryable 接口,而不必一次全部获取。类似:DbContext.Set&lt;Order&gt;().Where(o =&gt; o.CompanyID == cId) 将返回一个 IQuerayble,而不是所有数据。如果您将其传递给 OData,它将仅查询所需的记录。这意味着只从数据库中获取所需的数据。
    • 实体框架,不像我需要处理复杂的类型,比如连接和将字符串转换为日期和东西。我试图让我的问题保持简单
    猜你喜欢
    • 1970-01-01
    • 2014-10-05
    • 2017-11-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-09-20
    • 2021-10-08
    相关资源
    最近更新 更多