使用PagedList进行分页,效率如何?

时间:2015-04-08 10:29:01

标签: c# asp.net-mvc paging

我一直在尝试实现分页已经有一段时间了,我发现本教程用于使用MVC进行分页:ASP.NET MVC Paging Done Perfectly

现在,在此解决方案中,我在数据库中查询整个客户端,然后返回客户端的分页列表而不是普通列表。

我发现这令人不安,因为我只计划每页显示10或20个条目,而我的数据库很容易就会有超过一百万个。因此,每次我想要显示Index页面时查询整个数据库似乎都是一个不好的解决方案。

如果我理解错误的话,请立即给我剪掉,但对我而言,这个解决方案绝非完美。

我误解了什么吗?是否有更有效的解决方案或库可以用于MVC的分页?

7 个答案:

答案 0 :(得分:7)

自然地,分页将需要知道总结果计数,以便逻辑确定有多少页面等。但是,不是将所有结果都降低,而是将查询构建到数据库以返回分页量(例如30 )以及所有结果的计数。

例如,如果您使用的是Entity Framework或LINQ2SQL,则可以执行类似这样的操作

IQueryable<Result> allResults = MyRepository.RetrieveAll();

var resultGroup = allResults.OrderByDescending(r => r.DatePosted)
                                               .Skip(60)
                                               .Take(30)
                                               .GroupBy(p => new {Total = allResults.Count()})
                                               .First();

var results = new ResultObject
{
    ResultCount = resultGroup.Key.Total,
    Results = resultGrouping.Select(r => r)
};

因为我们在结果集上没有完成.ToList(),直到我们完成了我们想要的结果,我们还没有将结果带入内存。这是在我们在结果集上调用.First()时完成的。

最后,我们最终得到的Object(ResultObject)可用于稍后进行分页。由于我们有计数,我们已经知道我们在哪个页面(3我们跳过60,每页30)并且我们有结果显示。

进一步阅读和信息

How To: Page through Query Results

Server Side Paging with Entity Frame

答案 1 :(得分:4)

github上的示例说明它正在使用一个IQueryable,然后由ToPagedList()使用,这意味着代码已经过优化,并且本身不会返回所有记录......

查看类PagedList

的代码
// superset is the IQueryable.
TotalItemCount = superset == null ? 0 : superset.Count();

// add items to internal list
if (superset != null && TotalItemCount > 0)
    Subset.AddRange(pageNumber == 1
    ? superset.Skip(0).Take(pageSize).ToList()
    : superset.Skip((pageNumber - 1) * pageSize).Take(pageSize).ToList()

因为你可以看到它已经使用推荐的服务器端分页方法skip和take,然后预先形成ToList()。

但是,如果您没有使用IQueryable,即IEnumerable,则使用以下代码:

/// <summary>
/// Initializes a new instance of the <see cref="PagedList{T}"/> class that divides the supplied superset into subsets the size of the supplied pageSize. The instance then only containes the objects contained in the subset specified by index.
/// </summary>
/// <param name="superset">The collection of objects to be divided into subsets. If the collection implements <see cref="IQueryable{T}"/>, it will be treated as such.</param>
/// <param name="pageNumber">The one-based index of the subset of objects to be contained by this instance.</param>
/// <param name="pageSize">The maximum size of any individual subset.</param>
/// <exception cref="ArgumentOutOfRangeException">The specified index cannot be less than zero.</exception>
/// <exception cref="ArgumentOutOfRangeException">The specified page size cannot be less than one.</exception>
public PagedList(IEnumerable<T> superset, int pageNumber, int pageSize)
        : this(superset.AsQueryable<T>(), pageNumber, pageSize)
    {
    }

问题是,根据用于获取IEnumerable的过滤器可能包含所有记录,因此尽可能使用IQueryable以获得PagedList的最佳性能。

答案 2 :(得分:2)

如果你转到PagedList addon的github页面,你可以看到,如果你有一个返回IQueryable<T>的方法,那么PagedList魔术可以处理它而不返回数据库中的每个项目。如果您无法控制数据库中的查询返回给您,那么您必须依赖其他方法。

该页面的示例是

public class ProductController : Controller
{
    public object Index(int? page)
    {
        var products = MyProductDataSource.FindAllProducts(); //returns IQueryable<Product> representing an unknown number of products. a thousand maybe?

        var pageNumber = page ?? 1; // if no page was specified in the querystring, default to the first page (1)
        var onePageOfProducts = products.ToPagedList(pageNumber, 25); // will only contain 25 products max because of the pageSize

        ViewBag.OnePageOfProducts = onePageOfProducts;
        return View();
    }
}

答案 3 :(得分:2)

链接教程看起来很奇怪,因为它使用的是List<Client>。这确实会将所有客户端带入内存,然后通过它进行分页。相反,您应该查找使用IQueryable<T>的方法,特别是SkipTake,因此分页应该看起来像

IQueryable<Client> clients = repo.GetClients();          // lazy load - does nothing
List<Client> paged = clients.Skip(20).Take(10).ToList(); // execute final SQL

根据您使用的映射器,您将在EF,NHibernate,Linq-to-SQL等中找到类似的方法

答案 4 :(得分:1)

您可以通过三种方式在应用程序中实现分页:

  • 强制您的Repository以最少量的数据将DTO返回给客户端,并使用一些jquery插件自行提供分页。这是一种简单的方法,但有时(如你的情况),这不是一个选项。所以你必须实现服务器端分页
  • 缓存整个集合,并返回包含LINQ扩展名的所需页面。我认为许多存储库和ORM在内部都这样做(不能与Entity Framework一起使用,不能肯定地说)。此解决方案的问题在于您必须同步缓存和数据库,并且您必须使服务器具有足够的内存来存储您的所有数据(或转到云或其他内容)。与其他答案一样,您可以使用惰性IEnumerable工作跳过不需要的数据,因此您不需要缓存该集合。
  • 在DB端实现分页。如果您使用SQL,则可以使用ROW_NUMBER构建,它可以在MS SQLOracleMySQL中使用(不是ROW_NUMBER实际上,只有模拟)。如果您有NoSQL解决方案,那么您必须检查文档。

答案 5 :(得分:0)

此组件(PagedList)适用于大量记录,第一次和每次选择页面时,它将对数据库进行2次调用。一个返回记录数,另一个只返回所选页面的记录。只要确保你没有调用ToList()方法

答案 6 :(得分:-1)

  1. .cshtml&amp;中带有Webgrid对象的代码它会很好。
  2. 分页复杂性非常低。
  3. 清洁代码。
  4. Micro Soft BCL类。少虫子。