webentwicklung-frage-antwort-db.com.de

Bessere Möglichkeit, eine Datenseite abzufragen und die Gesamtanzahl im Entity Framework 4.1 zu ermitteln?

Wenn ich derzeit eine Abfrage ausführen muss, die mit Paging verwendet wird, mache ich dies ungefähr so:

//Setup query (Typically much more complex)
var q = ctx.People.Where(p=>p.Name.StartsWith("A"));

//Get total result count prior to sorting
int total = q.Count();       

//Apply sort to query
q = q.OrderBy(p => p.Name);  

q.Select(p => new PersonResult
{
   Name = p.Name
}.Skip(skipRows).Take(pageSize).ToArray();

Das funktioniert, aber ich habe mich gefragt, ob es möglich ist, dies zu verbessern, um mit Linq noch effizienter zu sein. Ich konnte mir keine Möglichkeit vorstellen, die Zählung mit dem Datenabruf in einer einzigen Fahrt zur DB ohne gespeicherte Prozedur zu kombinieren.

50
C.J.

Bei der folgenden Abfrage werden die Anzahl und die Seitenergebnisse in einer einzigen Fahrt zur Datenbank abgerufen. Wenn Sie jedoch die SQL in LINQPad überprüfen, werden Sie feststellen, dass dies nicht sehr schön ist. Ich kann mir nur vorstellen, wie es bei einer komplexeren Abfrage aussehen würde.

var query = ctx.People.Where (p => p.Name.StartsWith("A"));

var page = query.OrderBy (p => p.Name)
                .Select (p => new PersonResult { Name = p.Name } )          
                .Skip(skipRows).Take(pageSize)
                .GroupBy (p => new { Total = query.Count() })
                .First();

int total = page.Key.Total;
var people = page.Select(p => p);

Bei einer einfachen Abfrage wie dieser können Sie wahrscheinlich eine der beiden Methoden verwenden (2 Fahrten in die Datenbank oder GroupBy, um dies in einer Reise zu tun) und dabei keinen großen Unterschied feststellen. Ich denke, für eine komplexe Lösung wäre eine gespeicherte Prozedur die beste Lösung.

73
Jeff Ogata

Jeff Ogatas Antwort kann etwas optimiert werden.

var results = query.OrderBy(p => p.Name)
                   .Select(p => new
                   {
                       Person = new PersonResult { Name = p.Name },
                       TotalCount = query.Count()
                   })          
                   .Skip(skipRows).Take(pageSize)
                   .ToArray(); // query is executed once, here

var totalCount = results.First().TotalCount;
var people = results.Select(r => r.Person).ToArray();

Das macht so ziemlich dasselbe, außer dass es die Datenbank nicht mit einem unnötigen GROUP BY stört. Wenn Sie nicht sicher sind, dass Ihre Abfrage mindestens ein Ergebnis enthält und Sie nicht möchten, dass jemals eine Ausnahme ausgelöst wird, können Sie totalCount auf folgende (jedoch weniger saubere) Weise erhalten:

var totalCount = results.FirstOrDefault()?.TotalCount ?? 0;
6
Rudey

Ich schlage vor, zwei Abfragen für die erste Seite vorzunehmen, eine für die Gesamtanzahl und eine für die erste Seite oder die Ergebnisse. 

Zwischenspeichern Sie die Gesamtzahl, wenn Sie über die erste Seite hinausgehen.

3
Bryan

Wichtiger Hinweis für Personen, die EF Core verwenden> = 1.1.x:

Zu der Zeit war ich auf der Suche nach einer Lösung für diese und diese Seite ist/war Rang 1 für den Google-Begriff "EF Core Paging Total Count".

Nach der Überprüfung des SQL-Profilers habe ich festgestellt, dass EF für jede zurückgegebene Zeile eine SELECT COUNT(*) generiert. Ich habe jede Lösung auf dieser Seite müde.

Dies wurde mit EF Core 2.1.4 & SQL Server 2014 getestet. Am Ende musste ich sie wie zwei separate Abfragen ausführen. Was für mich zumindest nicht das Ende der Welt ist.

var query = _db.Foo.AsQueryable(); // Add Where Filters Here.


var resultsTask = query.OrderBy(p => p.ID).Skip(request.Offset).Take(request.Limit).ToArrayAsync();
var countTask = query.CountAsync();

await Task.WhenAll(resultsTask, countTask);

return new Result()
{
    TotalCount = await countTask,
    Data = await resultsTask,
    Limit = request.Limit,
    Offset = request.Offset             
};

Es sieht so aus, als ob das EF Core Team sich dessen bewusst ist:

https://github.com/aspnet/EntityFrameworkCore/issues/13739https://github.com/aspnet/EntityFrameworkCore/issues/11186

0
SimonGates