用LINQ进行对象分页

你将如何在LINQ查询中实现分页? 其实暂时,我会满意,如果SQL TOPfunction可以被模仿。 不过,我相信无论如何,迟早会出现对全面分页支持的需求。

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

您正在寻找Skip and Take扩展方法。 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); 

使用SkipTake绝对是路要走。 如果我正在实现这一点,我可能会写我自己的扩展方法来处理分页(使代码更易读)。 这个实现当然可以使用Skip and Take

 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(编写数据库查询时,编译器将selectIQueryable版本)。

根据您的分页要求,您还可以添加一些额外的行为(例如处理负面pageSizepage值)。 这里是一个例子,你将如何在你的查询中使用这个扩展方法:

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

这是我使用LINQ to对象进行分页的高性能方法:

 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都不是很高效。

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

编辑 – 删除跳过(0),因为它是没有必要的

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

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

 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位作者。

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

批量显然是一个整数。 这利用了整数简单地丢下小数位的事实。

我半开玩笑地回应,但是会按照你的意愿去做,而且因为延期了,所以如果你这样做的话,你不会招致很大的性能损失

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

这个解决scheme不适用于LinqToEntities,我甚至不知道它是否可以把它变成一个好的查询。

我使用这种扩展方法:

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

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

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

有两个主要的select:

.NET> = 4.0 dynamicLINQ

  1. 使用System.Linq.Dynamic添加; 在顶部。
  2. 使用: var people = people.AsQueryable().OrderBy("Make ASC, Year DESC").ToList();

你也可以通过NuGet获得。

.NET <4.0 扩展方法

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