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:
public IQueryable<URL> GetAllUrls()
{
return context.Urls.AsQueryable();
}
und Anwendungsbeispiel:
repo.GetAllUrls().Where(u => <condition>).Take(10).ToList()
Was passiert da?
- Wir erhalten ein
IQueryable
Objekt (das noch nicht auf die Datenbank zugreift) mitrepo.GetAllUrls()
- Wir erstellen ein neues
IQueryable
Objekt mit der angegebenen Bedingung mit.Where(u => <condition>
- Wir erstellen ein neues
IQueryable
Objekt mit dem angegebenen Paging-Limit mit.Take(10)
- Wir rufen Ergebnisse aus der Datenbank mit ab
.ToList()
. Unser IQueryable
Objekt ist zu SQL (like select 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:
public async Task<IQueryable<URL>> GetAllUrlsAsync()
{
var urls = await context.Urls.ToListAsync();
return urls.AsQueryable();
}
Mit dem gleichen Anwendungsbeispiel haben wir:
- Wir laden alle Milliarden in Ihrer Datenbank gespeicherten URLs mit in den Speicher
await context.Urls.ToListAsync();
.
- Wir haben Speicherüberlauf. Richtiger Weg, um Ihren Server zu töten
Über async / warten
Warum wird Async / Warten bevorzugt verwendet? Schauen wir uns diesen Code an:
var stuff1 = repo.GetStuff1ForUser(userId);
var stuff2 = repo.GetStuff2ForUser(userId);
return View(new Model(stuff1, stuff2));
was geschieht hier?
- Beginnend in Zeile 1
var stuff1 = ...
- Wir senden eine Anfrage an den SQL Server, für den wir etwas Zeug1 bekommen möchten
userId
- Wir warten (aktueller Thread ist blockiert)
- Wir warten (aktueller Thread ist blockiert)
- ..... .....
- SQL Server senden uns Antwort
- Wir gehen zu Zeile 2 über
var stuff2 = ...
- Wir senden eine Anfrage an den SQL Server, für den wir etwas Zeug2 bekommen möchten
userId
- Wir warten (aktueller Thread ist blockiert)
- Und wieder
- ..... .....
- SQL Server senden uns Antwort
- Wir rendern Ansicht
Schauen wir uns also eine asynchrone Version davon an:
var stuff1Task = repo.GetStuff1ForUserAsync(userId);
var stuff2Task = repo.GetStuff2ForUserAsync(userId);
await Task.WhenAll(stuff1Task, stuff2Task);
return View(new Model(stuff1Task.Result, stuff2Task.Result));
was geschieht hier?
- Wir senden eine Anfrage an den SQL Server, um stuff1 zu erhalten (Zeile 1)
- Wir senden eine Anfrage an den SQL Server, um stuff2 zu erhalten (Zeile 2)
- Wir warten auf Antworten vom SQL Server, aber der aktuelle Thread ist nicht blockiert. Er kann Anfragen von anderen Benutzern bearbeiten
- Wir rendern Ansicht
Richtiger Weg, es zu tun
Also guter Code hier:
using System.Data.Entity;
public IQueryable<URL> GetAllUrls()
{
return context.Urls.AsQueryable();
}
public async Task<List<URL>> GetAllUrlsByUser(int userId) {
return await GetAllUrls().Where(u => u.User.Id == userId).ToListAsync();
}
Beachten Sie, dass Sie hinzufügen müssen, using System.Data.Entity
um die Methode ToListAsync()
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 nur await context.Urls.ToListAsync()
materialisiert verwenden und damit arbeiten List<Url>
.