复杂的LINQ分页算法

发布于 2024-10-20 07:12:02 字数 517 浏览 3 评论 0 原文

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

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

据我所知

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

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

更麻烦的是,我需要对报告进行寻呼机控制。所以我需要能够计算总页数。

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

编辑 - 我可能应该提到子项目通过自引用外键连接回项目,因此整个项目,父项目,子项目等等,都以 IQueryable 的形式返回。

We have a list of projects that may or may not have a collection of subprojects. Our report needs to contain all the projects except those that are the parent project of a subproject.

I need to page this into pages of, say, 25 rows. But if subprojects appear on that page then ALL the subprojects of that project must appear on the same page. So more than 25 items may appear if necessary.

I've got as far as

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

Obviously, this fails the second part of the requirements.

As a further pain in the arse, I need to have a pager control on the report. So I'll need to be able to calculate the total number of pages.

I could loop through the whole table of projects but the performance will suffer. Can anybody come up with a paged solution?

EDIT - I should probably mention that SubProjects joins back onto Projects via a selfreferencing foreign key so the whole lot, parent projects, subprojects and all, comes back as an IQueryable<Project>.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

我们的影子 2024-10-27 07:12:03

也许可以尝试对父项目进行分页,但仅显示其子项目。

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>
}

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

Perhaps try paging the parent projects instead, but only displaying its children.

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

Then just loop through that project's sub projects when displaying the data.

@model IEnumerable<Project>

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

Now all subprojects with the same parent will be on the same page.

暮年 2024-10-27 07:12:03

我发现通过将关注点从查询中抽象出来,处理分页更容易。这是我使用的解决方案的来源。这将使您的代码更加直接,并将所有分页逻辑转换为对 ToPagedList(int index, int pageSize) 的单个简单调用。我不是 100% 确定,但我相信我最初从 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
}

I find its easier to deal with pagination by abstracting the concern away from the query. Here is the source for the solution I use. This will make your code much more straight-forward and transform all the paging logic into a single, simple call to ToPagedList(int index, int pageSize). I'm not 100% sure, but I believe I originally ganked this source from Rob Conery's Kona project (his blog is at http://blog.wekeroad.com/).

The Helper Class

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);
    }
}

The Interfaces

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

The PagedList Implementation

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
}
落叶缤纷 2024-10-27 07:12:03

对我来说,这听起来像这样:

    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>,其中 T 是子项目集合的类型(例如,列出<子项目>)。因此,基本上您要做的就是将项目分页为 25 个组,然后扩展子项目集合并将它们与父项目分组。然后,您可以按照自己的意愿显示它们,可能是通过使用嵌套的 foreach 循环它们。

What it sounds like to me is something like this:

    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;

This would make expandedPagedProjects an IEnumerable<IGrouping<Project, T>> where T is the type of your subproject collection (for example, a List<SubProject>). So basically what you'd be doing is paging the projects into groups of 25, then expanding the subproject collections and grouping them in with their parent project. You can then display them however you wish, probably by looping through them with a nested foreach.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文