复杂的LINQ分页算法

时间:2011-03-01 14:51:35

标签: c# linq linq-to-sql

我们有一个项目列表,可能有也可能没有子项目集合。我们的报告需要包含除子项目的父项目之外的所有项目。

我需要将此页面分成25行的页面。但是,如果子项目出现在该页面上,则该项目的所有子项目必须出现在同一页面上。因此,如有必要,可能会出现超过25个项目。

我到目前为止

var pagedProjects = db.Projects.Where(x => !x.SubProjects.Any()).Skip(
    (pageNo -1) * pageSize).Take(pageSize);

显然,这不符合要求的第二部分。

作为屁股的进一步痛苦,我需要对报告进行寻呼机控制。所以我需要能够计算总页数。

我可以遍历整个项目表,但性能会受到影响。任何人都可以提出分页解决方案吗?

编辑 - 我应该提一下,SubProjects通过自引用外键加入到项目中,所以整个批次,父项目,子项目和所有项目都以IQueryable<Project>的形式返回。

3 个答案:

答案 0 :(得分:1)

也许尝试分页父项目,但只显示其子项。

var pagedParentProjects = db.Projects.Where(x => x.SubProjects.Any()).Skip(pageNo-1) * pageSize).Take(pageSize);

然后在显示数据时循环浏览该项目的子项目。

@model IEnumerable<Project>

@foreach (Project project in Model)
{
    <div>
        @foreach (Project subProject in project.SubProjects)
        {
            <span>subProject.Name</span>
        }
    </div>
}

现在所有具有相同父级的子项目将位于同一页面上。

答案 1 :(得分:1)

通过从查询中抽象出问题,我发现更容易处理分页。这是我使用的解决方案的来源。这将使您的代码更直接,并将所有分页逻辑转换为对ToPagedList(int index, int pageSize)的单个简单调用。我不是百分百肯定,但我相信我最初从Rob Conery的Kona项目中获取了这个来源(他的博客位于http://blog.wekeroad.com/)。

助手类

public static class Pagination
{
    public static PagedList<T> ToPagedList<T>(this IEnumerable<T> source, int index)
    {
        return new PagedList<T>(source.AsQueryable(), index, 10);
    }
    public static PagedList<T> ToPagedList<T>(this IEnumerable<T> source, int index, int pageSize)
    {
        return new PagedList<T>(source.AsQueryable(), index, pageSize);
    }
    public static PagedList<T> ToPagedList<T>(this IQueryable<T> source, int index, int pageSize)
    {
        return new PagedList<T>(source, index, pageSize);
    }

    public static PagedList<T> ToPagedList<T>(this IQueryable<T> source, int index)
    {
        return new PagedList<T>(source, index, 10);
    }
}

接口

public interface IPagedList<T> : IList<T>, IPagedList
{
}
public interface IPagedList
{
    int TotalCount { get; set; }
    int TotalPages { get; set; }
    int PageIndex { get; set; }

    int PageSize { get; set; }

    bool IsPreviousPage { get; }

    bool IsNextPage { get; }
}

PagedList实施

public class PagedList<T> : List<T>, IPagedList<T>
{
    public PagedList(IQueryable<T> source, int index, int pageSize)
    {
        int total = source.Count();
        this.TotalCount = total;
        this.TotalPages = total/pageSize;

        if (total%pageSize > 0)
            TotalPages++;

        this.PageSize = pageSize;
        this.PageIndex = index;
        this.AddRange(source.Skip(index*pageSize).Take(pageSize).ToList());
    }

    public PagedList(IEnumerable<T> source, int total, int index, int pageSize)
    {
        this.TotalCount = total;
        this.TotalPages = total/pageSize;

        if (total%pageSize > 0)
            TotalPages++;


        this.PageSize = pageSize;
        this.PageIndex = index;
        this.AddRange(source);
    }

    #region IPagedList<T> Members

    public int TotalPages { get; set; }

    public int TotalCount { get; set; }

    public int PageIndex { get; set; }

    public int PageSize { get; set; }

    public bool IsPreviousPage
    {
        get { return (PageIndex > 0); }
    }

    public bool IsNextPage
    {
        get { return (PageIndex*PageSize) <= TotalCount; }
    }

    #endregion
}

答案 2 :(得分:0)

对我来说听起来像是这样的:

    var pagedProjects = db.Projects.Skip((pageNo - 1) * pageSize).Take(pageSize);

    var expandedPagedProjects = from p in pagedProjects
                                group p.subprojects by p into grp
                                select grp;

这会使expandedPagedProjects成为IEnumerable<IGrouping<Project, T>>,其中T是子项目集合的类型(例如,List<SubProject>)。所以基本上你要做的就是将项目分成25个组,然后扩展子项目集合并将它们与父项目分组。然后,您可以根据需要显示它们,可能是通过使用嵌套的foreach循环遍历它们。