使用带有 OData feed C# 的存储过程的自定义分页,没有 Entity Framework

Custom Paging using a stored procedure with OData feed C# without Entity Framework

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

数据首先从数据库派生,数据库是使用连接的 5 个表的组合,这使得它不适合与 Entity Framework 一起使用,除了 Entity Framework 真的很慢(一个控制器的 45k 条记录)。

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

的情况下执行此操作

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

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

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

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

在不知道您的全部要求的情况下,我假设此数据的最终目的地是 Power Bi 报告。因此,我建议您完全跳过特殊的分页代码,让 Power Bi 桌面直接连接到 SQL 服务器以访问表格。

Power Bi desktop 将尽最大努力为您重新创建关系。如果您的原始表和 ID 以直截了当的方式命名,Power Bi 桌面可以很好地为您重新创建关系。试一试。

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

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

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

我最终找到的解决方案是通过扩展 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);