Paging avec LINQ pour les objects

Comment implémenteriez-vous la pagination dans une requête LINQ? En fait, pour le moment, je serais satisfait si la fonction sql TOP pouvait être imitée. Cependant, je suis sûr que le besoin d’une assistance complète en matière de pagination se fera plus tard plus tard.

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

    Vous recherchez les méthodes d’extension Skip and Take . Skip passe au-delà des N premiers éléments du résultat, renvoyant le rest; Take renvoie les N premiers éléments du résultat, supprimant tous les éléments restants.

    Consultez MSDN pour plus d’informations sur l’utilisation de ces méthodes: http://msdn.microsoft.com/en-us/library/bb386988.aspx

    Par exemple:

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

    Utiliser Skip and Take est certainement la voie à suivre. Si je l’implémentais, j’écrirais probablement ma propre méthode d’extension pour gérer la pagination (pour rendre le code plus lisible). L’implémentation peut bien sûr utiliser la Skip and Take :

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

    La classe définit deux méthodes d’extension – une pour IEnumerable et une pour IQueryable , ce qui signifie que vous pouvez l’utiliser avec LINQ to Objects et LINQ to SQL (lors de l’écriture de la requête de firebase database, le compilateur choisira la version IQueryable ).

    Selon vos besoins en matière de pagination, vous pouvez également append un comportement supplémentaire (par exemple, gérer une pageSize page ou page valeur de page négative). Voici un exemple d’utilisation de cette méthode d’extension dans votre requête:

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

    Voici mon approche performante de la pagination lorsqu’on utilise LINQ pour des objects:

     public static IEnumerable> Page(this IEnumerable source, int pageSize) { Contract.Requires(source != null); Contract.Requires(pageSize > 0); Contract.Ensures(Contract.Result>>() != null); using (var enumerator = source.GetEnumerator()) { while (enumerator.MoveNext()) { var currentPage = new List(pageSize) { enumerator.Current }; while (currentPage.Count < pageSize && enumerator.MoveNext()) { currentPage.Add(enumerator.Current); } yield return new ReadOnlyCollection(currentPage); } } } 

    Cela peut alors être utilisé comme ça:

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

    Aucune de ces erreurs ne sera évitée si vous êtes intéressé par plusieurs pages.

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

    EDIT – Supprimé Skip (0) car ce n’est pas nécessaire

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

    Je ne sais pas si cela aidera quelqu’un, mais je l’ai trouvé utile pour mes objectives:

     private static IEnumerable PagedIterator(IEnumerable 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++; } } 

    Pour utiliser ceci, vous auriez une requête linq et passer le résultat avec la taille de la page dans une boucle 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 } 

    Donc, cela va parcourir chaque auteur à la recherche de 100 auteurs à la fois.

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

    Batchsize sera évidemment un entier. Cela tire parti du fait que les entiers ne font que laisser tomber des décimales.

    Je blague à moitié avec cette réponse, mais cela fera ce que vous voulez, et comme il est reporté, vous ne subirez pas de pénalité importante si vous le faites.

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

    Cette solution n’est pas pour LinqToEntities, je ne sais même pas si cela pourrait transformer cela en une bonne requête.

    Semblable à la réponse de Lukazoid, j’ai créé une extension pour IQueryable.

      public static IEnumerable> PageIterator(this IQueryable source, int pageSize) { Contract.Requires(source != null); Contract.Requires(pageSize > 0); Contract.Ensures(Contract.Result>>() != null); using (var enumerator = source.GetEnumerator()) { while (enumerator.MoveNext()) { var currentPage = new List(pageSize) { enumerator.Current }; while (currentPage.Count < pageSize && enumerator.MoveNext()) { currentPage.Add(enumerator.Current); } yield return new ReadOnlyCollection(currentPage); } } } 

    Il est utile si Skip ou Take ne sont pas pris en charge.

    J’utilise cette méthode d’extension:

     public static IQueryable Page(this IQueryable obj, int page, int pageSize, System.Linq.Expressions.Expression> 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 GetAll(int RowIndex, int PageSize, ssortingng SortExpression) { int totalRows; int pageIndex = RowIndex / PageSize; List data= new List(); IEnumerable 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 prection = null) { this.setsPerPage = setsPerPage; this.pageNumber = pageNumber > 0 ? pageNumber - 1 : pageNumber; if (!ValidatePagerByPageNumber(pageNumber)) return this; var rowList = rows.Cast(); 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 }; } 

    c'est ce que j'ai fait. Normalement, vous commencez à 1 mais dans IList vous commencez avec 0. Donc, si vous avez 152 lignes, cela signifie que vous avez 8 paging mais dans IList vous avez seulement 7. hop cela peut rendre la chose plus claire pour vous

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

    Il y a deux options principales:

    .NET> = 4.0 LINQ dynamic :

    1. Ajouter en utilisant System.Linq.Dynamic; au sumt.
    2. Utilisez: var people = people.AsQueryable().OrderBy("Make ASC, Year DESC").ToList();

    Vous pouvez également l’obtenir par NuGet .

    Méthodes d’extension .NET <4.0 :

     private static readonly Hashtable accessors = new Hashtable(); private static readonly Hashtable callSites = new Hashtable(); private static CallSite> GetCallSiteLocked(ssortingng name) { var callSite = (CallSite>)callSites[name]; if(callSite == null) { callSites[name] = callSite = CallSite>.Create( Binder.GetMember(CSharpBinderFlags.None, name, typeof(AccessorCache), new CSharpArgumentInfo[] { CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) })); } return callSite; } internal static Func GetAccessor(ssortingng name) { Func accessor = (Func)accessors[name]; if (accessor == null) { lock (accessors ) { accessor = (Func)accessors[name]; if (accessor == null) { if(name.IndexOf('.') >= 0) { ssortingng[] props = name.Split('.'); CallSite>[] 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 OrderBy(this IEnumerable source, ssortingng property) { return Enumerable.OrderBy(source, AccessorCache.GetAccessor(property), Comparer.Default); } public static IOrderedEnumerable OrderByDescending(this IEnumerable source, ssortingng property) { return Enumerable.OrderByDescending(source, AccessorCache.GetAccessor(property), Comparer.Default); } public static IOrderedEnumerable ThenBy(this IOrderedEnumerable source, ssortingng property) { return Enumerable.ThenBy(source, AccessorCache.GetAccessor(property), Comparer.Default); } public static IOrderedEnumerable ThenByDescending(this IOrderedEnumerable source, ssortingng property) { return Enumerable.ThenByDescending(source, AccessorCache.GetAccessor(property), Comparer.Default); }