it-swarm.com.de

Paging mit LINQ für Objekte

Wie würden Sie Paging in einer LINQ-Abfrage implementieren? Eigentlich wäre ich vorerst zufrieden, wenn die SQL TOP-Funktion imitiert werden könnte. Ich bin mir jedoch sicher, dass der Bedarf an vollständiger Paging-Unterstützung ohnehin früher besteht.

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

Sie suchen nach den Erweiterungsmethoden Skip und Take. Skip bewegt sich an den ersten N Elementen im Ergebnis vorbei und gibt den Rest zurück. Take gibt die ersten N Elemente im Ergebnis zurück und löscht alle verbleibenden Elemente.

Weitere Informationen zur Verwendung dieser Methoden finden Sie unter MSDN: http://msdn.Microsoft.com/en-us/library/bb386988.aspx

Zum Beispiel:

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

Die Verwendung von Skip und Take ist definitiv der richtige Weg. Wenn ich dies implementieren würde, würde ich wahrscheinlich eine eigene Erweiterungsmethode schreiben, um mit dem Paging (um den Code lesbarer zu machen) umzugehen. Die Implementierung kann natürlich Skip und Take verwenden:

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

Die Klasse definiert zwei Erweiterungsmethoden - eine für IEnumerable und eine für IQueryable. Dies bedeutet, dass Sie sie sowohl mit LINQ to Objects als auch mit LINQ to SQL verwenden können (beim Schreiben einer Datenbankabfrage wählt der Compiler die IQueryable-Version aus).

Abhängig von Ihren Paging-Anforderungen können Sie auch zusätzliches Verhalten hinzufügen (z. B. um negative pageSize- oder page-Werte zu behandeln). Hier ein Beispiel, wie Sie diese Erweiterungsmethode in Ihrer Abfrage verwenden würden:

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

Hier ist mein performanter Ansatz zum Paging, wenn LINQ für Objekte verwendet wird:

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

Dies kann dann so verwendet werden:

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

Keiner dieser Müll Skip und Take, der äußerst ineffizient sein wird, wenn Sie sich für mehrere Seiten interessieren.

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

Ich weiß nicht, ob das jemandem helfen wird, aber ich fand es nützlich für meine Zwecke:

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

Um dies zu verwenden, hätten Sie eine linq-Abfrage und übergeben das Ergebnis zusammen mit der Seitengröße in einer foreach-Schleife:

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
}

Dies wird also jeden Autor durchlaufen, der 100 Autoren gleichzeitig abruft.

4
Bitfiddler

BEARBEITEN - Übersprungen (0) wurde entfernt, da dies nicht erforderlich ist

var queryResult = (from o in objects where ...
                      select new
                      {
                          A = o.a,
                          B = o.b
                      }
                  ).Take(10);
4
Jack Marchetti
var pages = items.Select((item, index) => new { item, Page = index / batchSize }).GroupBy(g => g.Page);

Die Batchgröße wird offensichtlich eine ganze Zahl sein. Dadurch wird die Tatsache ausgenutzt, dass Ganzzahlen einfach Dezimalstellen weglassen.

Ich mache nur einen Scherz mit dieser Antwort, aber sie wird tun, was Sie wollen, und weil sie verzögert wird, werden Sie keinen großen Leistungsnachteil erleiden, wenn Sie dies tun

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

Diese Lösung ist nicht für LinqToEntities, ich weiß nicht einmal, ob daraus eine gute Abfrage werden könnte.

2
Todd A. Stedel

Ähnlich wie Lukazoids Antwort Ich habe eine Erweiterung für IQueryable erstellt.

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

Dies ist nützlich, wenn Skip oder Take nicht unterstützt werden.

1

Es gibt zwei Hauptoptionen:

.NET> = 4.0Dynamic LINQ :

  1. Fügen Sie using System.Linq.Dynamic hinzu. oben.
  2. Verwendung: var people = people.AsQueryable().OrderBy("Make ASC, Year DESC").ToList();

Sie können es auch über NuGet erhalten.

.NET <4.0Erweiterungsmethoden :

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

Ich benutze diese Erweiterungsmethode:

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

normalerweise fangen Sie bei 1 an, aber in IList beginnen Sie mit 0 . Wenn Sie also 152 Zeilen haben, bedeutet das, dass Sie 8 Paging haben. In IList haben Sie jedoch nur 7 ..__ kann die Sache für Sie klar machen 

0
Alen.Toma

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

0
Debendra Dash