Bagaimana Anda menerapkan paging dalam kueri LINQ? Sebenarnya untuk saat ini, saya akan puas jika fungsi sql TOP bisa ditiru. Namun, saya yakin bahwa kebutuhan untuk dukungan paging penuh akan muncul lebih cepat nanti.
var queryResult = from o in objects
where ...
select new
{
A = o.a,
B = o.b
}
????????? TOP 10????????
Menggunakan
Skip
danTake
jelas merupakan cara yang tepat. Jika saya menerapkan ini, saya mungkin akan menulis metode ekstensi saya sendiri untuk menangani paging (untuk membuat kode lebih mudah dibaca). Penerapannya tentu saja dapat menggunakanSkip
danTake
: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); } }
Kelas mendefinisikan dua metode ekstensi - satu untuk
IEnumerable
dan satu lagi untukIQueryable
, yang berarti Anda dapat menggunakannya dengan LINQ ke Objek dan LINQ ke SQL (saat menulis kueri database, kompilator akan memilihIQueryable
versinya).Bergantung pada persyaratan halaman Anda, Anda juga dapat menambahkan beberapa perilaku tambahan (misalnya untuk menangani negatif
pageSize
ataupage
nilai). Berikut adalah contoh bagaimana Anda akan menggunakan metode ekstensi ini dalam kueri Anda:var q = (from p in products where p.Show == true select new { p.Name }).Page(10, pageIndex);
sumber
IEnumerable
antarmuka daripadaIQueryable
ini akan menarik seluruh tabel database, yang akan menjadi hit kinerja utama.IQueryable
membuatnya berfungsi dengan kueri databse juga (saya mengedit jawabannya dan menambahkannya). Sangat disayangkan bahwa Anda tidak dapat menulis kode dengan cara yang sepenuhnya umum (di Haskell hal ini dapat dilakukan dengan kelas tipe). Pertanyaan asli menyebutkan LINQ ke Objek, jadi saya menulis hanya satu kelebihan beban.Berikut adalah pendekatan performant saya untuk paging saat menggunakan LINQ ke objek:
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); } } }
Ini kemudian dapat digunakan seperti ini:
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 } }
Tak satu pun dari sampah ini
Skip
danTake
yang akan sangat tidak efisien jika Anda tertarik pada banyak halaman.sumber
Paginate
untuk menghapusnoun
vsverb
ambiguitas.( for o in objects where ... select new { A=o.a, B=o.b }) .Skip((page-1)*pageSize) .Take(pageSize)
sumber
Tidak tahu apakah ini akan membantu siapa pun, tetapi saya merasa ini berguna untuk tujuan saya:
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++; } }
Untuk menggunakan ini, Anda akan memiliki beberapa query linq, dan meneruskan hasilnya bersama dengan ukuran halaman ke loop 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 }
Jadi ini akan mengulang setiap penulis yang mengambil 100 penulis sekaligus.
sumber
EDIT - Lewati Dihapus (0) karena tidak perlu
var queryResult = (from o in objects where ... select new { A = o.a, B = o.b } ).Take(10);
sumber
Take
10,Skip
0 mengambil 10 elemen pertama.Skip
0 tidak ada gunanya dan tidak boleh dilakukan. Dan urutanTake
danSkip
masalah -Skip
10,Take
10 mengambil elemen 10-20;Take
10,Skip
10 tidak mengembalikan elemen.var pages = items.Select((item, index) => new { item, Page = index / batchSize }).GroupBy(g => g.Page);
Batchsize jelas akan menjadi bilangan bulat. Ini mengambil keuntungan dari fakta bahwa bilangan bulat hanya menghilangkan tempat desimal.
Saya setengah bercanda dengan tanggapan ini, tetapi itu akan melakukan apa yang Anda inginkan, dan karena ditangguhkan, Anda tidak akan dikenai penalti kinerja yang besar jika melakukannya.
Solusi ini bukan untuk LinqToEntities, saya bahkan tidak tahu apakah ini bisa mengubahnya menjadi kueri yang bagus.
sumber
Mirip dengan jawaban Lukazoid, saya telah membuat ekstensi untuk IQuerable.
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); } } }
Berguna jika Lewati atau Ambil tidak didukung.
sumber
Saya menggunakan metode ekstensi ini:
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(); }
sumber
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 }; }
inilah yang saya lakukan. Normaly Anda mulai dari 1 tetapi di IList Anda mulai dengan 0. jadi jika Anda memiliki 152 baris yang berarti Anda memiliki 8 halaman tetapi di IList Anda hanya memiliki 7. hop ini dapat memperjelas bagi Anda
sumber
sumber
Ada dua opsi utama:
.NET> = 4.0 Dynamic LINQ :
var people = people.AsQueryable().OrderBy("Make ASC, Year DESC").ToList();
Anda juga bisa mendapatkannya dengan NuGet .
.NET <4.0 Metode Ekstensi :
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); }
sumber