Ich arbeite mit Entity Framework 6 an einigen Web-API-Dingen. Eine meiner Controller-Methoden ist "Get All", bei der erwartet wird, dass der Inhalt einer Tabelle aus meiner Datenbank als empfangen wird IQueryable<Entity>
. In meinem Repository frage ich mich, ob es einen vorteilhaften Grund gibt, dies asynchron zu tun, da ich neu in der Verwendung von EF mit Async bin.
Im Grunde läuft es darauf hinaus
public async Task<IQueryable<URL>> GetAllUrlsAsync()
{
var urls = await context.Urls.ToListAsync();
return urls.AsQueryable();
}
vs.
public IQueryable<URL> GetAllUrls()
{
return context.Urls.AsQueryable();
}
Wird die asynchrone Version hier tatsächlich Leistungsvorteile bringen, oder entsteht unnötiger Overhead, wenn ich zuerst auf eine Liste projiziere (wohlgemerkt asynchron) und dann zu IQueryable gehe?
c#
entity-framework
async-await
Jesse Carter
quelle
quelle
Antworten:
Das Problem scheint zu sein, dass Sie falsch verstanden haben, wie asynchron / warten Sie mit Entity Framework arbeiten.
Informationen zum Entity Framework
Schauen wir uns also diesen Code an:
und Anwendungsbeispiel:
Was passiert da?
IQueryable
Objekt (das noch nicht auf die Datenbank zugreift) mitrepo.GetAllUrls()
IQueryable
Objekt mit der angegebenen Bedingung mit.Where(u => <condition>
IQueryable
Objekt mit dem angegebenen Paging-Limit mit.Take(10)
.ToList()
. UnserIQueryable
Objekt ist zu SQL (likeselect top 10 * from Urls where <condition>
) kompiliert . Und die Datenbank kann Indizes verwenden. Der SQL Server sendet Ihnen nur 10 Objekte aus Ihrer Datenbank (nicht alle Milliarden in der Datenbank gespeicherten URLs).Okay, schauen wir uns den ersten Code an:
Mit dem gleichen Anwendungsbeispiel haben wir:
await context.Urls.ToListAsync();
.Über async / warten
Warum wird Async / Warten bevorzugt verwendet? Schauen wir uns diesen Code an:
was geschieht hier?
var stuff1 = ...
userId
var stuff2 = ...
userId
Schauen wir uns also eine asynchrone Version davon an:
was geschieht hier?
Richtiger Weg, es zu tun
Also guter Code hier:
Beachten Sie, dass Sie hinzufügen müssen,
using System.Data.Entity
um die MethodeToListAsync()
für IQueryable zu verwenden.Beachten Sie, dass Sie nicht arbeiten müssen, wenn Sie kein Filtern, Blättern und ähnliches benötigen
IQueryable
. Sie können nurawait context.Urls.ToListAsync()
materialisiert verwenden und damit arbeitenList<Url>
.quelle
GetAllUrlsByUser
Methode reagieren, müssen Sie sie nicht asynchronisieren. Geben Sie einfach die Aufgabe zurück und sparen Sie sich eine unnötige Zustandsmaschine, die vom Compiler nicht generiert wird.async
undawait
wenn Sie nichts mit der Liste tun. Lassen Sie den Anrufer dazuawait
. Wenn Sie zu diesem Zeitpunkt auf den Aufruf warten,return await GetAllUrls().Where(u => u.User.Id == userId).ToListAsync();
erstellen Sie einen zusätzlichen asynchronen Wrapper, wenn Sie die Assembly dekompilieren und die IL anzeigen.Es gibt einen massiven Unterschied in dem Beispiel, das Sie gepostet haben, der ersten Version:
Dies ist schlecht , es gibt im Grunde genommen
select * from table
alle Ergebnisse in den Speicher zurück und wendet dann daswhere
gegen das in der Speichersammlung an, anstattselect * from table where...
gegen die Datenbank.Die zweite Methode trifft die Datenbank erst dann, wenn eine Abfrage auf die angewendet wird
IQueryable
(wahrscheinlich über eine.Where().Select()
Operation im Linq- Stil, die nur die DB-Werte zurückgibt , die mit der Abfrage übereinstimmen.Wenn Ihre Beispiele vergleichbar
async
wären , wäre die Version normalerweise pro Anforderung etwas langsamer, da die Zustandsmaschine, die der Compiler generiert, um dieasync
Funktionalität zu ermöglichen, mehr Overhead verursacht .Der Hauptunterschied (und der Vorteil) besteht jedoch darin, dass die
async
Version mehr gleichzeitige Anforderungen zulässt, da der Verarbeitungsthread nicht blockiert wird, während auf den Abschluss der E / A gewartet wird (Datenbankabfrage, Dateizugriff, Webanforderung usw.).quelle
Kurz gesagt,
IQueryable
dient dazu, den RUN-Prozess zu verschieben und den Ausdruck zunächst in Verbindung mit anderenIQueryable
Ausdrücken zu erstellen. Anschließend wird der Ausdruck als Ganzes interpretiert und ausgeführt.Aber
ToList()
Methode (oder ein paar solche Methoden) sollen den Ausdruck sofort "so wie er ist" ausführen.Ihre erste Methode (
GetAllUrlsAsync
) wird sofort ausgeführt, da daraufIQueryable
dieToListAsync()
Methode folgt . Daher wird es sofort ausgeführt (asynchron) und gibt eine Reihe vonIEnumerable
s zurück.In der Zwischenzeit wird Ihre zweite Methode (
GetAllUrls
) nicht ausgeführt. Stattdessen wird ein Ausdruck zurückgegeben, und CALLER dieser Methode ist für die Ausführung des Ausdrucks verantwortlich.quelle