使用带有 OData 提要 C# 的存储过程的自定义分页,没有实体框架

Posted

技术标签:

【中文标题】使用带有 OData 提要 C# 的存储过程的自定义分页,没有实体框架【英文标题】:Custom Paging using a stored procedure with OData feed C# without Entity Framework 【发布时间】:2018-07-13 09:24:47 【问题描述】:

我想知道是否有人可以帮助我,一段时间以来,我一直试图弄清楚如何在 OData 提要 (v4) Web API 2 中实现自定义分页以提供 Power Bi 提要,但没有成功。

数据首先来自数据库,数据库是使用连接的 5 个表的组合,这使得它不适合与 Entity Framework 一起使用,除了 Entity Framework 非常慢(一个控制器的 45k 记录) .

我尝试了许多不同的方法,从设置记录总数以欺骗框架并用列表的空成员填充分页结果,到下面更基本的示例。但是,如果不从控制器返回大量记录,我仍然无法获取客户端(Power BI)正确获取分页结果。请查看简化的查询和代码,非常欢迎任何帮助,因为似乎没有明确的示例说明如何在不使用实体框架的情况下执行此操作。

下面的代码有效,但我一直遇到相同问题的变体,框架在返回后对列表进行分页,尽管我在此之前做了什么

T-SQL 存储过程:

CREATE PROCEDURE [dbo].[GetOrders] @CompanyID int,
                                   @Skip INT,
                                   @Take INT
AS

BEGIN 

SET NOCOUNT ON;

SELECT *
FROM Orders 
WHERE CompanyID = @CompanyID
ORDER BY t.OrderID
OFFSET @Skip ROWS FETCH NEXT @Take  ROWS ONLY

END

指向调用上述查询的仓库的控制器

[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;

框架的分页是在return response;之后完成的。

【问题讨论】:

您是否有理由不使用OFFSETFETCH 进行SQL 查询? @MarkC。没有特别的原因,我也可以使用 OFFSET 和 FETCH。 【参考方案1】:

在不了解您的全部要求的情况下,我假设这些数据的最终目的地是 Power Bi 报告。有了这个,我建议你完全跳过特殊的分页代码,让 Power Bi desktop 直接连接到 SQL Server 来访问表。

Power Bi desktop 将尽最大努力为您重建关系。如果您的原始表和 id 以严格的方式命名,Power Bi desktop 在为您重新创建关系方面做得相当不错。试试看吧。

导入完成后,您可以在click on the Relationship View 时检查关系。如果关系错误,请双击它以删除或编辑。

如果您担心每次运行报表都会损害数据库的性能,Power Bi 的默认模式是导入数据副本。如果数据小于 1GB,建议这样做。

对于较大的数据集,可以尝试DirectQuery 选项。 DirectQuery 是为响应加载更大数据集的需要而开发的,并且有其局限性(单一源数据库、无法处理过于复杂的查询、数据库性能以及某些视觉效果在功能上受到限制)。

【讨论】:

出于安全原因,我不能让 Power Bi 直接连接到数据库,它不是单个客户端数据库。 愚蠢的问题:您的 Power Bi 报告是否需要对您的数据集进行分页?如果不是,并且您的数据集小于 1GB,我仍然建议您在不分页的情况下对 ODATA 提要使用导入选项。 数据集太大,无法以 web api2 odata 分页的方式加载,开箱即用,一次性获取所有数据然后分页,除了只返回请求的数量。当一次性处理许多大表时,这是非常浪费的。它一次拉动不止一张桌子。 您是否尝试过使用 Power BI 中的参数和自定义函数来调用这些参数? youtube.com/watch?v=iiNDq2VrZPY【参考方案2】:

我最终找到的解决方案是通过扩展 EnableQueryAttribute 来中断框架(假设您关闭过滤等并设置最大页面大小)。在分页查询中,您需要在页面上方触发内部机制,此解决方案只是一种解决方法。关键是在“ApplyTo”之前将 Take 设置为 0。

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

分页属性

public sealed class PagingAttribute : EnableQueryAttribute

    public override IQueryable ApplyQuery(IQueryable queryable, ODataQueryOptions queryOptions)
    
        var result = default(IQueryable);
        var originalRequest = queryOptions.Request;

        var skip = queryOptions.Skip == null ? 0 : queryOptions.Skip.Value;
        var take = queryOptions.Top == null ? PageSize : queryOptions.Top.Value;

        queryOptions = ODataQueryOptionsUtilities.Transform(queryOptions, new ODataQueryOptionsUtilitiesTransformSettings  Skip = (map, option) => default(int?) );

        if (queryOptions.Request.ODataProperties().TotalCount != null)
            originalRequest.ODataProperties().TotalCount = originalRequest.GetInlineCount();

        result = queryOptions.ApplyTo(queryable);

        if (skip + take <= originalRequest.ODataProperties().TotalCount)
            originalRequest.ODataProperties().NextLink = NextPageLink.GetNextNewPageLink(originalRequest, (skip + take));

        return result;
    

我在控制器中设置了以下内容

        originalRequest.ODataProperties().TotalCount = Query.Item1; // Total size of all records to be returned
        originalRequest.SetInlineCount(Query.Item1);

【讨论】:

以上是关于使用带有 OData 提要 C# 的存储过程的自定义分页,没有实体框架的主要内容,如果未能解决你的问题,请参考以下文章

使用带有 C# 的 Entity Framework 6 调用现有的存储过程

Azure CosmosDB。存储过程中的继续令牌长度

带有两个OUT参数和c#的Mysql存储过程

OData V4 日期比较

如何对来自 PowerQuery 的 OData 中的大型数据集的请求进行分页?

如何使用c#执行返回多个游标的oracle存储过程