使用LINQ为对象分页

时间:2010-03-04 15:24:18

标签: c# .net linq paging

如何在LINQ查询中实现分页? 实际上,如果可以模仿sql TOP函数,我会感到满意。但是,我确信无论如何都需要尽快提供完整的寻呼支持。

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

12 个答案:

答案 0 :(得分:198)

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

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

例如:

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

答案 1 :(得分:49)

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

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

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

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

答案 2 :(得分:29)

这是我在使用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);
        }
    }
}

然后可以这样使用:

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

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

答案 3 :(得分:8)

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

答案 4 :(得分:5)

编辑 - 删除跳过(0),因为没有必要

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

答案 5 :(得分:5)

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

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

要使用它,您将获得一些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
}

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

答案 6 :(得分:3)

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

Batchsize显然是一个整数。这利用了整数只是丢弃小数位的事实。

我对这个回应是开玩笑的,但是它会做你想要的,并且因为它推迟了,如果你这样做,你不会招致很大的性能损失

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

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

答案 7 :(得分:2)

Lukazoid's answer类似,我为IQueryable创建了一个扩展名。

   public static IEnumerable<IEnumerable<T>> PageIterator<T>(this IQueryable<T> source, int pageSize)
            {
                Contract.Requires(source != null);
                Contract.Requires(pageSize > 0);
                Contract.Ensures(Contract.Result<IEnumerable<IQueryable<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);
                    }
                }
            }

如果不支持Skip或Take,这很有用。

答案 8 :(得分:1)

我使用这种扩展方法:

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

答案 9 :(得分:1)

data = data.Where(x => x != 0).ToArray();

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

答案 10 :(得分:1)

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

答案 11 :(得分:1)

有两个主要选项:

.NET&gt; = 4.0 Dynamic LINQ

  1. 使用System.Linq.Dynamic添加;在顶部。
  2. 使用:var people = people.AsQueryable().OrderBy("Make ASC, Year DESC").ToList();
  3. 您也可以通过NuGet获取它。

    .NET&lt; 4.0 Extension Methods

    private static readonly Hashtable accessors = new Hashtable();
    
    private static readonly Hashtable callSites = new Hashtable();
    
    private static CallSite<Func<CallSite, object, object>> GetCallSiteLocked(string name) {
        var callSite = (CallSite<Func<CallSite, object, object>>)callSites[name];
        if(callSite == null)
        {
            callSites[name] = callSite = CallSite<Func<CallSite, object, object>>.Create(
                        Binder.GetMember(CSharpBinderFlags.None, name, typeof(AccessorCache),
                    new CSharpArgumentInfo[] { CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) }));
        }
        return callSite;
    }
    
    internal static Func<dynamic,object> GetAccessor(string name)
    {
        Func<dynamic, object> accessor = (Func<dynamic, object>)accessors[name];
        if (accessor == null)
        {
            lock (accessors )
            {
                accessor = (Func<dynamic, object>)accessors[name];
                if (accessor == null)
                {
                    if(name.IndexOf('.') >= 0) {
                        string[] props = name.Split('.');
                        CallSite<Func<CallSite, object, object>>[] arr = Array.ConvertAll(props, GetCallSiteLocked);
                        accessor = target =>
                        {
                            object val = (object)target;
                            for (int i = 0; i < arr.Length; i++)
                            {
                                var cs = arr[i];
                                val = cs.Target(cs, val);
                            }
                            return val;
                        };
                    } else {
                        var callSite = GetCallSiteLocked(name);
                        accessor = target =>
                        {
                            return callSite.Target(callSite, (object)target);
                        };
                    }
                    accessors[name] = accessor;
                }
            }
        }
        return accessor;
    }
    public static IOrderedEnumerable<dynamic> OrderBy(this IEnumerable<dynamic> source, string property)
    {
        return Enumerable.OrderBy<dynamic, object>(source, AccessorCache.GetAccessor(property), Comparer<object>.Default);
    }
    public static IOrderedEnumerable<dynamic> OrderByDescending(this IEnumerable<dynamic> source, string property)
    {
        return Enumerable.OrderByDescending<dynamic, object>(source, AccessorCache.GetAccessor(property), Comparer<object>.Default);
    }
    public static IOrderedEnumerable<dynamic> ThenBy(this IOrderedEnumerable<dynamic> source, string property)
    {
        return Enumerable.ThenBy<dynamic, object>(source, AccessorCache.GetAccessor(property), Comparer<object>.Default);
    }
    public static IOrderedEnumerable<dynamic> ThenByDescending(this IOrderedEnumerable<dynamic> source, string property)
    {
        return Enumerable.ThenByDescending<dynamic, object>(source, AccessorCache.GetAccessor(property), Comparer<object>.Default);
    }