【问题标题】:Paging in Entity Framework实体框架中的分页
【发布时间】:2009-06-26 15:39:23
【问题描述】:

在实体框架中,使用LINQ to Entities,数据库分页通常以如下方式完成:

int totalRecords = EntityContext.Context.UserSet.Count;
var list     = EntityContext.Context.UserSet
                 .Skip(startingRecordNumber)
                 .Take(pageSize)
                 .ToList();

这会导致 两次 数据库调用。

请告诉,如何将其减少为 ONE 数据库调用。

谢谢。

【问题讨论】:

  • 在 EF 中会导致错误,您必须在调用 Skip 之前调用 OrderBy :) 如果您更新代码会很好。有人可能会浪费大量时间从帖子中复制代码。
  • 诀窍在stackoverflow.com/questions/7767409/…,但最好有简单的设计和2个调用

标签: entity-framework


【解决方案1】:

两个电话有什么问题?它们是小而快速的查询。数据库旨在支持大量小型查询。

开发一个复杂的解决方案来执行一个分页查询不会给您带来太多回报。

【讨论】:

    【解决方案2】:

    使用 Esql 并将存储过程映射到实体可以解决问题。 SP 将返回 totalRows 作为输出参数,返回当前页面作为结果集。

    CREATE PROCEDURE getPagedList(
    @PageNumber int,
    @PageSize int,
    @totalRecordCount int OUTPUT
    AS
    
    //Return paged records
    

    请指教。

    谢谢。

    【讨论】:

    • +1 不错。那会给你1个数据库调用。您仍然会执行两个查询,但它们会很快而且很小。
    【解决方案3】:

    嗯...使用分页的实际调用是第二个调用 - 那是一个调用。

    第二个调用是确定总行数 - 这是一个完全不同的操作,我不知道有什么方法可以将这两个不同的操作结合到一个使用实体框架的数据库调用中。

    问题是:您真的需要总行数吗?做什么的?这是否值得第二次数据库调用?

    另一个选项是使用 EntityObjectSource(在 ASP.NET 中),然后将其绑定到例如一个 GridView,并在 GridView 上启用 AllowPaging 和 AllowSorting 等,让 ASP.NET 运行时处理检索和显示相应数据页面的所有细节工作。

    马克

    【讨论】:

    • 您需要总记录数,以便知道分页界面中有多少页。我想知道你是否可以只做一个 list.Count 之类的?
    • 嗯,List.Count 可能会从数据库中获取所有行——这绝对不是你想要的。另外,我很确定 Linq to Entities 会返回一个空集,您要求提供超出实际数据集的页面 - 再说一遍:为什么需要总行数?真的吗?在您的页面上显示“第 5 页,共 17 页”当然很好 - 没有它你能活吗?
    • 感谢您的回复。 UI 需要一个分页栏,因此需要总记录数。
    • 如果您对静态数据进行分页,totalRecords 将相同,并且不应在每次页面更改时检索。如果页面之间的数据可能会发生变化,这可能会使用户感到困惑。
    • 谢谢。目标是反映当前行(包括新的添加和删除)。
    【解决方案4】:
    ALTER proc [dbo].[GetNames]
        @lastRow bigint,
        @pageSize bigint,
        @totalRowCount bigint output
    as
    begin
    
    select @totalRowCount = count(*) from _firstNames, _lastNames
    
    select
        FirstName,
        LastName,
        RowNumber
    from
    (
        select
            fn.[FirstName] as FirstName,
            ln.[Name] as LastName,
            row_number() over( order by FirstName ) as RowNumber
        from
            _firstNames fn, _lastNames ln
    ) as data
    where
        RowNumber between ( @lastRow + 1 ) and ( @lastRow + @pageSize )
    
    end 
    

    没有办法在一次调用中实现这一点,但这足够快。

    【讨论】:

    【解决方案5】:

    这个查询对于 DBManager 来说太小了,我不明白你为什么要这样做,无论如何要将它减少到一个数据库调用,请使用这个:

    var list     = EntityContext.Context.UserSet
                     .Skip(startingRecordNumber)
                     .Take(pageSize)
                     .ToList();
    int totalRecords = list.Count;
    

    【讨论】:

    • OP 想要计算所有记录而不是分页记录。在您的解决方案中,totalRecords 将等于 pageSize
    【解决方案6】:

    假设您想获取 pagesize=4 的第 2 页的详细信息

    int page =2;
    int pagesize=4;
    
    var pagedDetails= Categories.Skip(pagesize*(page-1)).Take(pagesize)
    .Join(Categories.Select(item=>new {item.CategoryID,Total = Categories.Count()}),x=>x.CategoryID,y=>y.CategoryID,(x,y)=>new {Category = x,TotalRows=y.Total});
    

    输出将包含 Category 和 TotalRows 的所有详细信息。

    一个数据库调用。

    生成的 SQL

    -- Region Parameters
    DECLARE @p0 Int = 2
    DECLARE @p1 Int = 4
    -- EndRegion
    SELECT [t2].[CategoryID], [t2].[CategoryName], [t2].[Description], [t2].[Picture], [t5].[value] AS [TotalRows]
    FROM (
        SELECT [t1].[CategoryID], [t1].[CategoryName], [t1].[Description], [t1].[Picture], [t1].[ROW_NUMBER]
        FROM (
            SELECT ROW_NUMBER() OVER (ORDER BY [t0].[CategoryID], [t0].[CategoryName]) AS [ROW_NUMBER], [t0].[CategoryID], [t0].[CategoryName], [t0].[Description], [t0].[Picture]
            FROM [Categories] AS [t0]
            ) AS [t1]
        WHERE [t1].[ROW_NUMBER] BETWEEN @p0 + 1 AND @p0 + @p1
        ) AS [t2]
    INNER JOIN (
        SELECT [t3].[CategoryID], (
            SELECT COUNT(*)
            FROM [Categories] AS [t4]
            ) AS [value]
        FROM [Categories] AS [t3]
        ) AS [t5] ON [t2].[CategoryID] = [t5].[CategoryID]
    ORDER BY [t2].[ROW_NUMBER]
    

    【讨论】:

    • 错了。它总是计算所有类别。即使在主查询上应用了 where。而且,它不返回 Category 实体,而是一个匿名类型,并且对每个类别一次又一次地计数。
    • 要求是: 1- 获取具有页面大小的页面的详细信息 2- 确保您在一次 DB CALL 中取回所有信息。我希望你明白你在说什么......你看到上面的要求了吗?您是否尝试过我提供的解决方案,或者您只是通过查看来分享您的想法?如果您尝试,这将是一个数据库调用...请尝试然后发表评论。当然,类别实体没有总计数属性,但我们仍然希望它在同一个调用中返回...返回类型是具有具体属性 Category 和 TotalRows 的匿名类。
    • 一个问题。如果他们想要Categories.Where(c => c.Name.Contains("a"))怎么办?
    • int page =1;诠释页面大小=4; var pagedDetails= Categories.Where(c => c.CategoryName.Contains("a")).Skip(pagesize*(page-1)).Take(pagesize) .Join(Categories.Select(item=>new {item .CategoryID,Total = Categories.Count()}) ,x=>x.CategoryID,y=>y.CategoryID,(x,y)=>new {Category = x,TotalRows=y.Total});这会工作
    • 我认为您很容易从我提供的示例中弄清楚...这是修改后的查询 int page =1;诠释页面大小=3; var pagedDetails= Categories.Where(c => c.CategoryName.Contains("a")) .Skip(pagesize*(page-1)).Take(pagesize) .Join(Categories.Select(item=>new {item .CategoryID,Total = Categories.Where(c => c.CategoryName.Contains("a")).Count()}) ,x=>x.CategoryID,y=>y.CategoryID,(x,y)= >new {类别 = x,TotalRows=y.Total}); pagedDetails.Dump();
    猜你喜欢
    • 1970-01-01
    • 2012-04-26
    • 2022-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多