Mit IEnumerable<T> und IQueryable<T> in ein generisches repository
Ich habe gelesen, der Anzahl der Beiträge, die in Bezug auf die Implementierung eines generischen repository. Ich habe auch gelesen eine Reihe von Beiträgen, die erklären, den Unterschied zwischen freilegen IEnumerable vs. IQueryable aus meinem repository.
Möchte ich die Flexibilität, dass meine Daten gefiltert, die in der Datenbank (anstatt in den Speicher durch den client), wollen aber vermeiden, dass die Definition eines eigenen repository-Schnittstelle für alle meine Einheiten (und konkreten Klassen, die diese Schnittstellen implementieren).
Soweit mein repository sieht wie folgt aus:
public interface IRepository<T>
{
IEnumerable<T> GetAll();
IEnumerable<T> Find(Expression<Func<T, bool>> where);
void Add(T entity);
void Attach(T entity);
void Delete(T entity);
}
und ein Beispiel für eine konkrete Umsetzung:
public class Repository<T> : IRepository<T> where T : class
{
private DbContext _context;
private DbSet<T> _entitySet;
public Repository(DbContext context)
{
_context = context;
_entitySet = _context.Set<T>();
}
public IEnumerable<T> GetAll()
{
return _entitySet;
}
public IEnumerable<T> Find(Expression<Func<T, bool>> where)
{
return _entitySet.Where(where);
}
public void Add(T entity)
{
_entitySet.Add(entity);
}
public void Attach(T entity)
{
_entitySet.Attach(entity);
}
public void Delete(T entity)
{
_entitySet.Remove(entity);
}
}
In diesem Fall mein repository verwendet DbContext
also was ich gerne wissen würde ist, wie das funktioniert mit dem generischen interface:
IQueryable<T>
stammt ausIEnumerable<T>
. In Meiner Methode find bin ich wieder einesIQueryable<T>
- Objekt, aber der client sieht nur diese alsIEnumerable<T>
. Bedeutet das, dass wenn ich tragen alle nachfolgenden Abfragen auf dieIEnumerable<T>
Objekt, das es tatsächlich die Operationen auf der Datenbank und nur die Rückkehr des Ergebnisse (weil das Objekt ist einIQueryable
in diesem Fall)? Oder,- Nur die
Where
- Klausel, die übergeben wird in derFind
Methode ausgeführt, der die Datenbank und alle nachfolgenden Abfragen, die ausgeführt werden, auf dieIEnumerable<T>
- Objekt auf dem client ausgeführt werden. Oder, - Keines von beiden geschehen, und ich habe Total falsch verstanden, wie
IEnumarable<T>
,IQueryable<T>
undLinq
funktioniert.
Update:
Ich bin eigentlich ziemlich überrascht über die Antworten, die ich erhalten haben, in die Kommentare. Meine original-repository zurückgegeben IQueryable und weitere Forschung führte mich zu glauben, das war eine schlechte Sache zu tun (z.B. wenn mein viewModel akzeptiert ein repository, in dessen Konstruktor aufgerufen werden, jede Abfrage, die es will, das macht es schwieriger ist zu testen).
Alle Lösungen, die ich gesehen habe, so weit einzubeziehen, erstellen von entity-spezifische repositories, so dass IQueryable ist nicht ausgesetzt (Der einzige Unterschied, den ich denke, ist, dass ich Tue dies, in einer Allgemeinen Weise).
- Soweit ich das beurteilen kann, alles aber
IQueryable<T>
erfolgt über den Speicher. Ich würde zurückIQueryable<T>
stattIEnumerable<T>
können Sie immer vonIQueryable<T>
zuIEnumerable<T>
wenn Sie fertig sind mit der Filterung. Und sowieso, wenn Sie einem Zu.Liste() oder beim Durchlaufen derIQueryable<T>
geht es automatisch alsIEnumerable<T>
.
Du musst angemeldet sein, um einen Kommentar abzugeben.
Da sind Sie wieder
IEnumerable<T>
alle nachfolgenden Aufrufe werden gemacht in Erinnerung (lokal).Eine Sache im Auge zu behalten ist, dass LINQ arbeitet mit einer Reihe von erweiterungsmethoden. Es sind erweiterungsmethoden für
IQueryable<T>
welche alle notwendigen Kabel zum durchführen von Abfragen in anderen Orten als lokal und Erweiterung der Methoden für dieIEnumerable<T>
, die nur lokal arbeiten.Beachten Sie, dass die von diesen ausgewählt wird, basierend auf der compile-Zeit-Typ, nicht der Lauf der Zeit geben. So ein
IQueryable
gegossen, die auf eineIEnumerable
behandelt werden alsIEnumerable
. Dies ist anders als die Art und Weise Klasse normalerweise arbeiten (Dank Polymorphismus) erlaubt jedoch den Ruf Website, die Kontrolle über wie Sie diese Vorgänge durchführen.Ein Beispiel, wo dies nützlich ist, wenn Sie brauchen, um alle Datensätze in der Tabelle und zählen Sie Sie. Es gibt keine Notwendigkeit ausführen die Zählung in SQL, wenn Sie gehen, um all die Ergebnisse sowieso.
Find
Methode zur Unterstützung der gängigen Szenarien (wie paging). Jede weitere Filterung / srting würde einfach durchgeführt werden, auf dem client.IQueryable<T>
? Meiner Meinung nach RücksendungIEnumerable<T>
ist die schlechteste Lösung überhaupt: Sie haben noch die verzögerte Ausführung (die Abfrage nicht ausgeführt werden, wenn IhreFind
Methode fertig ist, aber beim aufzählen der zurückgegebenenIEnumerable<T>
). Wenn Sie aufzählen, nachdem der Rahmen ist bereits entsorgt Sie eine Ausnahme. Wenn Sie fügen Sie eineWhere
- Klausel, um die zurückgegebenenIEnumerable<T>
performance-überraschungen. Entweder zurückIQueryable<T>
oder materialisierte Ergebnisse wieList<T>
.IQueryable
weil es effektiv stellt den data access layer und bedeutet, dass die Abfrage-Logik können Lecks in anderen Bereichen des Codes. Ich habe auch gehörtIQueryable
ist schwieriger zu testen. Ihr Kommentar zu die Ausführung der Abfrage, war gut getimed; ich dachte eigentlich über dieses genaue Szenario und wurde über einen Kommentar, Fragen über diese.IQueryable
zu ermöglichen, Abfragen an beliebigen stellen in Ihrem code. Wenn Sie das nicht wollen, möchten Sie vielleicht zu vermeiden, LINQ alle zusammen, und verwenden Sie eine andere underwiring, die macht, was Sie wollen.IEnumerable.Where
= client. Es spielt keine Rolle, wo es aufgelistet ist, es kommt darauf an, woWhere
ist.