C# 使用 LINQ 对对象进行分页

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2380413/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-07 01:56:36  来源:igfitidea点击:

Paging with LINQ for objects

c#.netlinqpaging

提问by user256890

How would you implement paging in a LINQ query? Actually for the time being, I would be satisfied if the sql TOP function could be imitated. However, I am sure that the need for full paging support comes up sooner later anyway.

您将如何在 LINQ 查询中实现分页?其实暂时如果能模仿sql TOP功能我就满足了。但是,我确信对完整分页支持的需求无论如何迟早会出现。

var queryResult = from o in objects
                  where ...
                  select new
                      {
                         A = o.a,
                         B = o.b
                      }
                   ????????? TOP 10????????

采纳答案by David Pfeffer

You're looking for the Skipand Takeextension methods. Skipmoves past the first N elements in the result, returning the remainder; Takereturns the first N elements in the result, dropping any remaining elements.

您正在寻找SkipTake扩展方法。Skip移过结果中的前 N ​​个元素,返回余数;Take返回结果中的前 N ​​个元素,删除所有剩余元素。

See MSDN for more information on how to use these methods: http://msdn.microsoft.com/en-us/library/bb386988.aspx

有关如何使用这些方法的更多信息,请参阅 MSDN:http: //msdn.microsoft.com/en-us/library/bb386988.aspx

Assuming you are already taking into account that the pageNumber should start at 0 (decrease per 1 as suggested in the comments) You could do it like this:

假设您已经考虑到 pageNumber 应该从 0 开始(按照评论中的建议每 1 减少)您可以这样做:

int numberOfObjectsPerPage = 10;
var queryResultPage = queryResult
  .Skip(numberOfObjectsPerPage * pageNumber)
  .Take(numberOfObjectsPerPage);

Otherwise as suggested by @Alvin

否则按照@Alvin的建议

int numberOfObjectsPerPage = 10;
var queryResultPage = queryResult
  .Skip(numberOfObjectsPerPage * (pageNumber - 1))
  .Take(numberOfObjectsPerPage);

回答by Hyman Marchetti

EDIT - Removed Skip(0) as it's not necessary

编辑 - 删除了 Skip(0) 因为它不是必需的

var queryResult = (from o in objects where ...
                      select new
                      {
                          A = o.a,
                          B = o.b
                      }
                  ).Take(10);

回答by Noel

   ( for o in objects
    where ...
    select new
   {
     A=o.a,
     B=o.b
   })
.Skip((page-1)*pageSize)
.Take(pageSize)

回答by Tomas Petricek

Using Skipand Takeis definitely the way to go. If I were implementing this, I would probably write my own extension method to handle paging (to make the code more readable). The implementation can of course use Skipand Take:

使用SkipTake绝对是要走的路。如果我要实现这个,我可能会编写自己的扩展方法来处理分页(使代码更具可读性)。实现当然可以使用SkipTake

static class PagingUtils {
  public static IEnumerable<T> Page<T>(this IEnumerable<T> en, int pageSize, int page) {
    return en.Skip(page * pageSize).Take(pageSize);
  }
  public static IQueryable<T> Page<T>(this IQueryable<T> en, int pageSize, int page) {
    return en.Skip(page * pageSize).Take(pageSize);
  }
}

The class defines two extension methods - one for IEnumerableand one for IQueryable, which means that you can use it with both LINQ to Objects and LINQ to SQL (when writing database query, the compiler will pick the IQueryableversion).

该类定义了两种扩展方法 - 一种 forIEnumerable和一种 for IQueryable,这意味着您可以将它与 LINQ to Objects 和 LINQ to SQL 一起使用(编写数据库查询时,编译器将选择IQueryable版本)。

Depending on your paging requirements, you could also add some additional behavior (for example to handle negative pageSizeor pagevalue). Here is an example how you would use this extension method in your query:

根据您的分页要求,您还可以添加一些额外的行为(例如处理负数pageSizepage值)。以下是如何在查询中使用此扩展方法的示例:

var q = (from p in products
         where p.Show == true
         select new { p.Name }).Page(10, pageIndex);

回答by Bitfiddler

Don't know if this will help anyone, but I found it useful for my purposes:

不知道这是否会帮助任何人,但我发现它对我的目的有用:

private static IEnumerable<T> PagedIterator<T>(IEnumerable<T> objectList, int PageSize)
{
    var page = 0;
    var recordCount = objectList.Count();
    var pageCount = (int)((recordCount + PageSize)/PageSize);

    if (recordCount < 1)
    {
        yield break;
    }

    while (page < pageCount)
    {
        var pageData = objectList.Skip(PageSize*page).Take(PageSize).ToList();

        foreach (var rd in pageData)
        {
            yield return rd;
        }
        page++;
    }
}

To use this you would have some linq query, and pass the result along with the page size into a foreach loop:

要使用它,您需要一些 linq 查询,并将结果与​​页面大小一起传递到 foreach 循环中:

var results = from a in dbContext.Authors
              where a.PublishDate > someDate
              orderby a.Publisher
              select a;

foreach(var author in PagedIterator(results, 100))
{
    // Do Stuff
}

So this will iterate over each author fetching 100 authors at a time.

因此,这将迭代每个作者,一次获取 100 个作者。

回答by Lukazoid

Here is my performant approach to paging when using LINQ to objects:

这是我在使用 LINQ to objects 时进行分页的高性能方法:

public static IEnumerable<IEnumerable<T>> Page<T>(this IEnumerable<T> source, int pageSize)
{
    Contract.Requires(source != null);
    Contract.Requires(pageSize > 0);
    Contract.Ensures(Contract.Result<IEnumerable<IEnumerable<T>>>() != null);

    using (var enumerator = source.GetEnumerator())
    {
        while (enumerator.MoveNext())
        {
            var currentPage = new List<T>(pageSize)
            {
                enumerator.Current
            };

            while (currentPage.Count < pageSize && enumerator.MoveNext())
            {
                currentPage.Add(enumerator.Current);
            }
            yield return new ReadOnlyCollection<T>(currentPage);
        }
    }
}

This can then be used like so:

然后可以像这样使用:

var items = Enumerable.Range(0, 12);

foreach(var page in items.Page(3))
{
    // Do something with each page
    foreach(var item in page)
    {
        // Do something with the item in the current page       
    }
}

None of this rubbish Skipand Takewhich will be highly inefficient if you are interested in multiple pages.

这些都不是垃圾SkipTake如果您对多个页面感兴趣,这将是非常低效的。

回答by Randy

I use this extension method:

我使用这种扩展方法:

public static IQueryable<T> Page<T, TResult>(this IQueryable<T> obj, int page, int pageSize, System.Linq.Expressions.Expression<Func<T, TResult>> keySelector, bool asc, out int rowsCount)
{
    rowsCount = obj.Count();
    int innerRows = rowsCount - (page * pageSize);
    if (innerRows < 0)
    {
        innerRows = 0;
    }
    if (asc)
        return obj.OrderByDescending(keySelector).Take(innerRows).OrderBy(keySelector).Take(pageSize).AsQueryable();
    else
        return obj.OrderBy(keySelector).Take(innerRows).OrderByDescending(keySelector).Take(pageSize).AsQueryable();
}

public IEnumerable<Data> GetAll(int RowIndex, int PageSize, string SortExpression)
{
    int totalRows;
    int pageIndex = RowIndex / PageSize;

    List<Data> data= new List<Data>();
    IEnumerable<Data> dataPage;

    bool asc = !SortExpression.Contains("DESC");
    switch (SortExpression.Split(' ')[0])
    {
        case "ColumnName":
            dataPage = DataContext.Data.Page(pageIndex, PageSize, p => p.ColumnName, asc, out totalRows);
            break;
        default:
            dataPage = DataContext.vwClientDetails1s.Page(pageIndex, PageSize, p => p.IdColumn, asc, out totalRows);
            break;
    }

    foreach (var d in dataPage)
    {
        clients.Add(d);
    }

    return data;
}
public int CountAll()
{
    return DataContext.Data.Count();
}

回答by Todd A. Stedel

var pages = items.Select((item, index) => new { item, Page = index / batchSize }).GroupBy(g => g.Page);

Batchsize will obviously be an integer. This takes advantage of the fact that integers simply drop decimal places.

Batchsize 显然是一个整数。这利用了整数简单地删除小数位的事实。

I'm half joking with this response, but it will do what you want it to, and because it's deferred, you won't incur a large performance penalty if you do

我对这个响应半开玩笑,但它会做你想做的事,而且因为它被推迟,如果你这样做,你不会招致很大的性能损失

pages.First(p => p.Key == thePage)

This solution is not for LinqToEntities, I don't even know if it could turn this into a good query.

此解决方案不适用于 LinqToEntities,我什至不知道它是否可以将其变成一个好的查询。

回答by Alen.Toma

    public LightDataTable PagerSelection(int pageNumber, int setsPerPage, Func<LightDataRow, bool> prection = null)
    {
        this.setsPerPage = setsPerPage;
        this.pageNumber = pageNumber > 0 ? pageNumber - 1 : pageNumber;
        if (!ValidatePagerByPageNumber(pageNumber))
            return this;

        var rowList = rows.Cast<LightDataRow>();
        if (prection != null)
            rowList = rows.Where(prection).ToList();

        if (!rowList.Any())
            return new LightDataTable() { TablePrimaryKey = this.tablePrimaryKey };
        //if (rowList.Count() < (pageNumber * setsPerPage))
        //    return new LightDataTable(new LightDataRowCollection(rowList)) { TablePrimaryKey = this.tablePrimaryKey };

        return new LightDataTable(new LightDataRowCollection(rowList.Skip(this.pageNumber * setsPerPage).Take(setsPerPage).ToList())) { TablePrimaryKey = this.tablePrimaryKey };
  }

this is what i did. Normaly you start at 1 but in IList you start with 0. so if you have 152 rows that mean you have 8 paging but in IList you only have 7. hop this can make thing clear for you

这就是我所做的。通常你从 1 开始,但在 IList 中你从 0 开始。所以如果你有 152 行,这意味着你有 8 个分页,但在 IList 中你只有 7。跳这可以让你清楚

回答by Debendra Dash

var results = (medicineInfo.OrderBy(x=>x.id)
                       .Skip((pages -1) * 2)
                       .Take(2));